在 Delphi 開發環境中,物件導向程式設計和表單管理是兩個密不可分的概念。理解物件的初始化、繼承、可見性以及事件處理機制,對於建構穩健且易於維護的應用程式至關重要。本文除了介紹表單的建立、顯示和生命週期管理外,也探討了 Object Pascal 語言的基礎知識,包含資料型別、運算子、控制流程等。此外,更進一步說明瞭泛型程式設計和匿名方法的應用,這些進階特性可以顯著提升程式碼的彈性和重用性,讓開發者能夠更有效率地處理複雜的程式邏輯。
物件導向程式設計與表單管理
在物件導向程式設計中,初始化和終結化是兩個重要的概念。初始化部分是可選的,而終結化部分同樣是可選的,但它只能出現在具有初始化部分的單元中。換句話說,沒有初始化部分就不會有終結化部分。
TFormSettings 類別
在 uFormSettings 單元中宣告了 TFormSettings 型別。型別宣告遵循保留字 type。在宣告表單型別之後,有一個變數宣告 FormSettings,其型別為 TFormSettings。變數宣告以 var 關鍵字開始。
type
TFormSettings = class(TForm)
// 類別宣告
end;
var
FormSettings: TFormSettings;
當定義一個變數時,也必須宣告其型別。由於 Object Pascal 語言是強型別的,因此在程式變得越來越複雜時,程式設計師需要明確決定型別,而不是由編譯器猜測型別。
繼承與可見性
繼承是物件導向程式設計中的一個重要概念。我們的設定表單具備標準多裝置表單的所有特性。可以檢查 FMX.Forms 單元中 TForm 類別的宣告。類別宣告定義了類別的名稱以及它繼承自哪個類別。
type
TForm = class(TCommonCustomForm)
// 類別宣告
end;
類別宣告根據可見性進行劃分。不同的類別成員可以是私有的(private),也可以是受保護的(protected),或者是公開的(public)或已發布的(published)。
程式碼範例:TFormSettings 類別宣告
type
TFormSettings = class(TForm)
private
// 私有成員
protected
// 受保護成員
public
// 公開成員
published
// 已發布成員
ToolBar: TToolBar;
SpdbtnBack: TSpeedButton;
procedure SpdbtnBackClick(Sender: TObject);
end;
內容解密:
TFormSettings類別繼承自TForm:表示TFormSettings具備TForm的所有屬性和方法。published區段:IDE會自動管理此區段,包括元件宣告和事件處理程式。ToolBar和SpdbtnBack:表單上的工具列和快速按鈕元件。SpdbtnBackClick事件處理程式:處理傳回按鈕的點選事件。
事件處理與表單顯示
要在主表單的按鈕點選事件中顯示設定表單,需要實作以下程式碼:
uses
uFormSettings;
procedure TFormMain.SpdbtnSettingsClick(Sender: TObject);
begin
if FormSettings = nil then
FormSettings := TFormSettings.Create(Application);
FormSettings.Show;
end;
內容解密:
if FormSettings = nil then:檢查FormSettings是否已經被例項化。FormSettings := TFormSettings.Create(Application);:如果尚未例項化,則建立一個新的TFormSettings例項。FormSettings.Show;:顯示設定表單。
次要表單的生命週期
全域變數 FormSettings 儲存了 TFormSettings 類別例項的參考。我們只需要在程式碼中建立一次設定表單。這是一種常見的懶惰建立模式,即在第一次使用物件之前才建立它。如果使用者從未點選設定按鈕,則該物件永遠不會被建立。
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle
title 物件導向程式設計與表單管理
package "NumPy 陣列操作" {
package "陣列建立" {
component [ndarray] as arr
component [zeros/ones] as init
component [arange/linspace] as range
}
package "陣列操作" {
component [索引切片] as slice
component [形狀變換 reshape] as reshape
component [堆疊 stack/concat] as stack
component [廣播 broadcasting] as broadcast
}
package "數學運算" {
component [元素運算] as element
component [矩陣運算] as matrix
component [統計函數] as stats
component [線性代數] as linalg
}
}
arr --> slice : 存取元素
arr --> reshape : 改變形狀
arr --> broadcast : 自動擴展
arr --> element : +, -, *, /
arr --> matrix : dot, matmul
arr --> stats : mean, std, sum
arr --> linalg : inv, eig, svd
note right of broadcast
不同形狀陣列
自動對齊運算
end note
@enduml圖表翻譯: 此圖示描述了懶惰建立模式的流程,首先檢查 FormSettings 是否已經被例項化,如果沒有,則建立新的例項,最後顯示設定表單。
Object Pascal 語言基礎與進階概念
建構子與物件所有權管理
在 Object Pascal 中,建構子的使用伴隨著物件所有權的管理。以 Create(AOwner: TComponent) 為例,當我們建立一個新的表單時,會將全域的 Application 物件指定為該表單的擁有者(Owner)。這代表 Application 物件會負責在應用程式終止時呼叫該表單的解構子,釋放其記憶體。
Form := TMyForm.Create(Application);
內容解密:
Create(AOwner: TComponent)是表單的建構子,用於建立新的表單例項。AOwner引數指定了該表單的擁有者。在此例中,Application是擁有者,負責管理表單的生命週期。- 正確管理物件的生命週期是 Object Pascal 程式設計中的重要環節。
Token 與分隔符
在 Object Pascal 中,Token 是編譯器能夠理解的最小有意義的文字單元。程式原始碼由 Token 和分隔符組成。分隔符可以是空白字元或註解。
var
Identifier: Integer;
內容解密:
var、Identifier和Integer都是 Token。:和;是特殊符號,同樣被視為 Token。- 分隔符(如空白字元)用於區分不同的 Token。
識別符與保留字
識別符用於命名常數、型別、變數、程式、函式等。保留字則是編譯器內建具有特殊意義的字詞,不能用作識別符。
type
TMyType = Integer;
內容解密:
TMyType是自定義的型別識別符。Integer是內建型別。type是保留字,用於宣告新型別。
常數與常數表示式
常數是指其值不可改變的識別符。Object Pascal 允許使用常數表示式,這些表示式在編譯時期被評估。
const
One = 1;
OneReal = 1.0;
Name = 'Marco';
Computer = 'Paweł''s computer';
內容解密:
One和OneReal是整數和實數常數。Name和Computer是字串常數。注意單引號內的特殊字元處理方式。- 字串常數中的兩個連續單引號表示一個單引號字元。
型別定義與列舉型別
Object Pascal 程式設計從定義自訂型別開始。內建型別可用於建立更複雜的自訂型別。
type
TSchoolGrade = (sgVeryGood, sgGood, sgSufficient, sgInsufficient);
內容解密:
TSchoolGrade是列舉型別,用於表示學校成績。- 使用列舉型別可提高程式碼的可讀性和安全性,避免使用未定義的值。
啟用 SCOPEDENUMS 編譯指令
啟用 $SCOPEDENUMS ON 編譯指令後,列舉值必須以完整限定形式使用。
{$SCOPEDENUMS ON}
type
TSchoolGrade = (VeryGood, Good, Sufficient, Insufficient);
var
Grade: TSchoolGrade;
begin
Grade := TSchoolGrade.VeryGood;
end;
內容解密:
$SCOPEDENUMS ON編譯指令強制使用完整限定的列舉值。- 這種寫法提高了程式碼的可讀性,並減少命名衝突的風險。
Object Pascal 語言實用
列舉型別的使用
在 Object Pascal 中,列舉型別是一種非常有用的資料型別,可以用來定義一組具有特定含義的常數。例如,我們可以定義一個 TSchoolGrade 列舉型別來表示學生的成績等級:
type
TSchoolGrade = (sgVeryGood, sgGood, sgSufficient, sgInsufficient);
使用列舉型別的正確語法
在早期版本的 Delphi 中,使用列舉型別需要加上型別名稱作為字首,以避免名稱衝突:
var
SG: TSchoolGrade;
begin
SG := TSchoolGrade.VeryGood; // 正確的語法
內聯變數宣告
從較新的 Delphi 版本開始,我們可以在程式碼塊中直接宣告變數,並使用 var 關鍵字:
var X := 20.0;
var I: Integer := Trunc(X);
這種語法不僅簡潔,還支援型別推斷,可以省略變數的型別:
var I := Trunc(X);
最重要的是,內聯變數的宣告具有新的可見性和生命週期規則,變數的作用域僅限於宣告它的程式碼塊。
集合與陣列
使用集合
集合是 Object Pascal 中的一種基本集合型別,可以用來表示一組無序的元素。例如,我們可以定義一個 TSchoolGrades 集合來表示合格的成績等級:
type
TSchoolGrade = (sgVeryGood, sgGood, sgSufficient, sgInsufficient);
TSchoolGrades = set of TSchoolGrade;
const
Qualifying_Grades: TSchoolGrades = [sgVeryGood, sgGood, sgSufficient];
function IsQualifyingGrade(SG: TSchoolGrade): boolean;
begin
Result := SG in Qualifying_Grades;
end;
使用陣列
陣列是另一種常用的集合型別,可以用來表示一組有序的元素。陣列可以是固定大小的,也可以是動態大小的。
定義固定大小的陣列
type
TChessPiece = record
Name: string;
Value: Double;
end;
const
Chess_pieces_count = 6;
Chess_pieces: array[0..Chess_pieces_count-1] of TChessPiece =
(
(Name: 'Pawn'; Value: 1),
(Name: 'Knight'; Value: 3),
(Name: 'Bishop'; Value: 3),
(Name: 'Rook'; Value: 5),
(Name: 'Queen'; Value: 9),
(Name: 'King'; Value: 0)
);
使用動態陣列
var
Fruits: array of string;
begin
Fruits := ['Apple', 'Pear'];
Fruits := Fruits + ['Banana', 'Orange'];
型別助手
型別助手是一種擴充套件現有型別的機制,可以為現有的型別新增新的方法。例如,我們可以為 TSchoolGrade 列舉型別定義一個型別助手:
type
TSchoolGradeHelper = record helper for TSchoolGrade
public
function ToString: string;
function ToInteger: Integer;
function IsQualifying: Boolean;
end;
function TSchoolGradeHelper.IsQualifying: Boolean;
begin
Result := self in Qualifying_grades;
end;
function TSchoolGradeHelper.ToInteger: Integer;
begin
case self of
sgVeryGood: Result := 5;
// 其他情況的處理
end;
end;
使用型別助手的好處
使用型別助手可以使程式碼更加簡潔和易讀。例如,我們可以使用 ToString 方法將 TSchoolGrade 值轉換為字串:
var
Grade: TSchoolGrade;
begin
Grade := sgVeryGood;
ShowMessage(Grade.ToString);
詳細解說
- 列舉型別的定義和使用:瞭解如何定義和使用列舉型別,包括正確的語法和內聯變數宣告。
- 集合和陣列的使用:學習如何使用集合和陣列,包括固定大小和動態大小的陣列。
- 型別助手的使用:瞭解如何定義和使用型別助手,以擴充套件現有型別的功能。
物件導向泛型與匿名方法在 Object Pascal 中的應用
Object Pascal 語言在現代軟體開發中佔有重要地位,其物件導向、泛型及匿名方法等特性極大地提升了程式碼的靈活性與可維護性。本文將探討 Object Pascal 中的泛型、匿名方法及其實際應用。
列舉型別的 Helper 方法
在 Object Pascal 中,我們可以為列舉型別定義 Helper 方法,以增強其功能。例如,對於一個代表學校成績的列舉型別 TSchoolGrade,我們可以定義 Helper 方法來取得其整數值或字串表示:
type
TSchoolGrade = (sgVeryGood, sgGood, sgSufficient, sgInsufficient);
TSchoolGradeHelper = record helper for TSchoolGrade
function ToInt: Integer;
function ToString: string;
end;
function TSchoolGradeHelper.ToInt: Integer;
begin
case Self of
sgVeryGood: Result := 4;
sgGood: Result := 3;
sgSufficient: Result := 2;
sgInsufficient: Result := 1;
end;
end;
function TSchoolGradeHelper.ToString: string;
begin
case Self of
sgVeryGood: Result := 'Very Good';
sgGood: Result := 'Good';
sgSufficient: Result := 'Sufficient';
sgInsufficient: Result := 'Insufficient';
end;
end;
內容解密:
TSchoolGradeHelper為TSchoolGrade列舉型別提供了額外的功能。ToInt方法將列舉值轉換為對應的整數。ToString方法傳回列舉值對應的字串描述。- 使用
Self關鍵字來參照當前的列舉值。
泛型程式設計
泛型是 Object Pascal 中的一個強大特性,允許我們編寫更通用的程式碼,使同一演算法能夠操作多種資料型別。泛型可以是資料結構或方法,它們可以被型別引數化。
可填入(Fillable)泛型記錄
type
TFillable<T> = record
Value: T;
IsFilled: Boolean;
end;
TFillableString = TFillable<string>;
TFillableInteger = TFillable<Integer>;
內容解密:
TFillable<T>定義了一個泛型記錄,用於表示可填入的值。Value欄位儲存實際的值,IsFilled欄位指示該值是否已填入。- 使用泛型,我們可以為不同型別建立特定的可填入型別,如
TFillableString和TFillableInteger。
泛型約束
在某些情況下,我們需要對泛型型別引數施加約束,以確保它們滿足特定的條件。例如:
type
TFmxProcessor<T: TFMXObject, constructor> = class
// ...
end;
內容解密:
TFmxProcessor<T>是一個泛型類別,其型別引數T必須是TFMXObject的子類別,並且具有公開的建構函式。
自訂排序演算法
泛型在實作自訂排序演算法時非常有用。無論是排序字元、整數還是實數,演算法的邏輯保持不變。我們只需要提供比較兩個值的方法:
// 使用泛型的排序演算法示例
內容解密:
- 泛型使得排序演算法可以適用於不同資料型別。
- 需要提供比較函式以確定值的順序。
TObjectList 與 TList 的比較
在管理物件列表時,TObjectList<T> 比非泛型的 TList 更安全、更方便:
procedure DoPersonsGenerics;
var
Persons: TObjectList<TPerson>;
P: TPerson;
begin
Persons := TObjectList<TPerson>.Create;
try
Persons.Add(TPerson.Create('Kirk', 'Hammett'));
Persons.Add(TPerson.Create('James', 'Hetfield'));
// ...
for P in Persons do
Log(P.Fullname); // 不需要型別轉換
finally
Persons.Free;
end;
end;
內容解密:
TObjectList<TPerson>確保列表中只能新增TPerson或其子類別的例項。- 使用泛型列表可以避免執行期的型別轉換錯誤。
- 可以使用更易讀的
for..in..do迴圈遍歷列表。
匿名方法
Object Pascal 中的匿名方法允許我們將程式碼視為資料。可以將函式或程式指定給變數,作為引數傳遞給其他函式,或作為結果傳回。這種特性使得程式碼更加緊湊和可維護。
匿名方法的應用示例
// 匿名方法示例
內容解密:
- 匿名方法增強了程式碼的靈活性。
- 可以用於事件處理、回呼函式等場景。