問題來源: http://www.cnblogs.com/del/archive/2008/04/26/973346.html#1171927
在 Delphi 中儲存系列對象, 大家常用 TList 類; 有了 TObjectList(在 Contnrs 單元)以後, 儲存物件就有了更好的選擇, 因為從 TObjectList 列表中移除的對象同時會得到釋放.
很少有人使用 TStringList 儲存對象, 殊不知用 TStringList 儲存對象也有 TList 和 TObjectList 所不及的優勢.
我想在繼續探討前先重複一個概念: 對象的 "指標" 和 "首地址":
我們通過對象的指標可以找到對象, 也就是說指標是指向了對象; 對象也不過是一系列資料, "指標" 一般是指向這組資料的 "首地址".
下面代碼可以擷取 Button1 對象的 "指標" 和 "首地址":
procedure TForm1.Button1Click(Sender: TObject);begin ShowMessageFmt('指標: %d', [Integer(@Button1)]); {14910416} ShowMessageFmt('首地址: %d', [Integer(Button1)]); {15011440}end;
我們再看下 TList、TObjectList 和 TStringList 添加對象的方法聲明:
TList : function Add(Item: Pointer): Integer;TObjectList : function Add(AObject: TObject): Integer;TStringList : function AddObject(const S: string; AObject: TObject): Integer;
可以看出, TList 添加的只是指標; TObjectList 和 TStringList 添加的類型是對象.
添加對象時, 是把整個對象的資料都添加進去嗎? 當然不是, 只要記住對象的首地址就可以了(應該也是用類似指標的辦法, 我沒仔細研究), 測試代碼:
procedure TForm1.Button1Click(Sender: TObject);var List: TStringList;begin ShowMessageFmt('指標: %d', [Integer(@Button1)]); {14910416} ShowMessageFmt('首地址: %d', [Integer(Button1)]); {15011440} List := TStringList.Create; List.AddObject('btn', Button1); ShowMessageFmt('取出: %d', [Integer(List.Objects[0])]); {15011440, 可以看出相同與上面的首地址} List.Free;end;
通過 TStringList 的 AddObject 和 InsertObject 方法可以添加對象;
用 Objects[] 屬性取出對象; 用 List[] 取出字串. 樣本:
procedure TForm1.Button1Click(Sender: TObject);var List: TStringList; obj: TObject; str: string;begin List := TStringList.Create; List.AddObject('btn', Button1); {添加} obj := List.Objects[0]; {取出對象} str := List[0]; {取出字串} {使用對象, 有個前提:我們知道它屬於 TButton} ShowMessage(TButton(obj).Caption); {Button1} ShowMessage(str); {btn} List.Free;end;
添加對象的指標可以嗎? 可以, 但需要轉換成無類型指標, 例:
procedure TForm1.Button1Click(Sender: TObject);var List: TStringList; obj: TObject;begin List := TStringList.Create; List.AddObject('btn', Pointer(@Button1)); List.AddObject('btn', Pointer(Button1)); {這樣也可以} obj := List.Objects[0]; ShowMessage(TButton(obj).Caption); {顯示: Button1} List.Free;end;
既然也可以添加指標, 那我們也可以添加不屬於 TObject 的結構等其他指標;
假如不能添加指標, 也將無法添加結構, 因為結構不屬於 TObject. 舉例:
type PMyRec = ^TMyrec; TMyRec = record s: string; i: Integer; end;procedure TForm1.Button1Click(Sender: TObject);var List: TStringList; R1,R2: TMyRec;begin List := TStringList.Create; R1.s := 'abc'; R1.i := 123; List.AddObject('rec', Pointer(@R1)); //List.AddObject('rec', @R1); {結構比較特殊, 不轉無類型指標也可以} R2 := PMyRec(List.Objects[0])^; ShowMessageFmt('%s,%d', [R2.s, R2.i]); {abc,123} List.Free;end;
前面說到 TStringList 還會有點優勢; 首先得承認它的劣勢, 因為它是兩組資料構成的列表, 在資料量特別大的時候效率上會有劣勢; 現在說說它的優勢:
從 TList 和 TObjectList 取出的物件類型是未知的(當然作者知道), 所以一般只能儲存單一類型的對象;
因為 TStringList 有兩個欄位, 我們可以用那個 String 欄位來儲存物件類型, 從而讓 TStringList 可以同時儲存更多類型的對象. 舉例:
unit Unit1;interfaceuses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); end;var Form1: TForm1;implementation{$R *.dfm}type PMyRec = ^TMyrec; TMyRec = record s: string; i: Integer; end;procedure TForm1.Button1Click(Sender: TObject);var List: TStringList; R1,R2: TMyRec; str: string; i: Integer;begin List := TStringList.Create; R1.s := 'abc'; R1.i := 123; str := '我是字串'; List.AddObject('1', @R1); {用 1 表示結構 TMyRec} List.AddObject('2', Sender); {用 2 表示 TButton} List.AddObject('3', Self); {用 3 表示 TForm1} List.AddObject('4', Pointer(str)); {用 4 表示 String} for i := 0 to List.Count - 1 do begin case StrToIntDef(List[i], 0) of 1: begin R2 := PMyRec(List.Objects[i])^; ShowMessageFmt('%s,%d', [R2.s, R2.i]); {abc,123} end; 2: ShowMessage(TButton(List.Objects[i]).Caption); {Button1} 3: ShowMessage(TForm1(List.Objects[i]).Text); {Form1} 4: ShowMessage(PChar(List.Objects[i])); {我是字串} end; end; List.Free;end;end.