在 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; interface uses 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.