(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)
快速导航:
一、概述
五、后记
一、概述
等了几百年,Delphi终于原生的支持泛型了。以前使用Delphi,泛型是不被支持的,但是可以用一些第三方库来实现间接的泛型容器支持。如HouSisong大虾编制的DGL泛型库,只需要创建几个简单的“头”文件,就可以拥有指定类型的容器集合类。DGL泛型库非常类似于STL泛型库,很容易上手,如果大家想知道具体使用方法,我另外开文章说明。
Delphi2009提供了几个好用的泛型容器,如TList<T>、TQueue<T>、TStack<T>、TDictionary<TKey, TValue>,还有针对于对象使用的TObjectList<T>等几个类。此外,还提供了TArray数组辅助静态类,封装了数组(array of T)的几个常用操作,如排序等。
但是在智能感知的时候,TList<T>等泛型集合提示好像有些BUG:

图1
为什么是“[]”,而不是“()”?
下面针对TList和TObjectList及两者的区别对Delphi2009的泛型功能进行初步体验。
二、体验TList<T>
在此,我将使用以前版本的指针集合类TList与TList<T>作对比,保存一组整形数据。使用控制台的方式进行程序编写。
program TestTList;2

3
{$APPTYPE CONSOLE}4

5
uses6
SysUtils,7
Classes,8
Generics.Collections; // 泛型集合命名空间,太优美了!9

10
var11
intList: TList<Integer>;12
oldList: TList;13
n, elem: Integer;14
pTmp: Pointer;15
begin16
// 以下代码测试旧的集合对象17
oldList := TList.Create;18
oldList.Add(Pointer(1));19
oldList.Add(Pointer(2));20
oldList.Add(Pointer(3));21

22
Writeln('TList start');23

24
for n := 0 to oldList.Count - 1 do25
begin26
Writeln(Integer(oldList[n]));27
end;28

29
for pTmp in oldList do30
begin31
Writeln(Integer(pTmp));32
end;33

34
FreeAndNil(oldList);35

36
// 以下代码测试整形泛型集合对象37
intList := TList<Integer>.Create;38
intList.Add(1);39
intList.Add(2);40
intList.Add(3);41

42
Writeln( #13 + #10 + 'TList<T> start');43

44
for n := 0 to intList.Count - 1 do45
begin46
Writeln(intList[n]);47
end;48

49
for elem in intList do50
begin51
Writeln(elem);52
end;53

54
FreeAndNil(intList);55

56
// ----------------------------------------------------------57
Writeln('press any key
');58
Readln;59
end.
运行结果:

图2
三、体验TObjectList<T>
刚开始看到TObjectList的时候我有点不解,既然是泛型,那么T就不区分值类型和引用类型,为什么还会多出来一个TObjectList<T>呢?在阅读了Generic.Collections的源码和经过试验后,我终于明白了原因,待我来慢慢分析。
同样,我将使用Contnrs命名空间下的TObjectList和TObjectList<T>做对比,使用控制台程序进行程序的编写。
首先创建一个类,该类在创建时,对象将打印“创建 ” + 对象的索引号,销毁时打印“销毁 ” + 对象的索引号:
2
3 interface
4
5 uses
6 SysUtils;
7
8 type
9 TFelix = class
10 private
11 fId: Integer;
12 public
13 constructor Create; virtual;
14 destructor Destroy; override;
15 property Id: Integer read fId write fId;
16 end;
17
18 var
19 gCount: Integer;
20
21 implementation
22
23 { TFelix }
24
25 constructor TFelix.Create;
26 begin
27 fId := gCount;
28 Writeln('Constructor Felix ' + IntToStr(fId));
29 Inc(gCount);
30 end;
31
32 destructor TFelix.Destroy;
33 begin
34 Writeln('Destructor Felix ' + IntToStr(fId));
35
36 inherited;
37 end;
38
39 end.
控制台程序代码:
program TestTObjectList;2

3
{$APPTYPE CONSOLE}4

5
uses6
SysUtils,7
Contnrs,8
Generics.Collections,9
Felix in 'Felix.pas';10

11
var12
objList: TObjectList<TFelix>;13
oldObjList: TObjectList;14
n: Integer;15
felix: TFelix;16
pFelix: Pointer;17
begin18
// 以下代码测试旧对象集合19
Writeln('TObjectList start');20

21
oldObjList := TObjectList.Create; // 1*22
for n := 0 to 2 do23
begin24
oldObjList.Add(TFelix.Create);25
end;26

27
for pFelix in oldObjList do28
begin29
Writeln(TFelix(pFelix).Id);30
end;31

32
FreeAndNil(oldObjList);33

34
// 以下代码测试泛型对象集合35
Writeln(#13 + #10 + 'TObjectList<T> start');36

37
objList := TObjectList<TFelix>.Create; // 2*38
for n := 0 to 2 do39
begin40
objList.Add(TFelix.Create);41
end;42

43
for felix in objList do44
begin45
Writeln(felix.Id);46
end;47

48
FreeAndNil(objList);49

50
// ----------------------------------------------------------51
Writeln('press any key
');52
Readln;53
end.

图3
如果我们将代码中的第1*处修改成:
oldObjList := TObjectList.Create(False);
将产生如下结果:
图4
相对于TObjectList<T>,没有Create(AOwnsObjects: Boolean)方式的重载,我们如何才能让TObjectList<T>“不拥有”对象,当TObjectList<T>中的元素重新赋值、TObjectList<T>集合对象销毁的时候,怎样能保证里面的旧元素不进行销毁操作呢?答案是:不能。
四:TList<T>和TObjectList<T>的区别
如果将上面代码的objList对象声明时改成TList<TFelix>类型,并将第2*处代码改成objList := TList<TFelix>.Create;结果就和使用TObjectList(图4)的效果一样了,当调用方法SetItem或者集合被销毁后,旧元素或者内部的元素不会被销毁。
原因在于,TObjectList<T>从TList<T>继承而来,只重写了Notify方法,此方法在集合内元素改变(指针变换、删除、添加)等操作时调用。
请阅读Generics.Collections.pas文件第1236行,TObjectList<T>重写了Notify方法后,先调用了超类的Notify方法,然后判断操作如果为cnRemoved则将元素.Free,所以元素在此处被析构。
五:后记
使用TObjectList<T>会将对象自动销毁,使用TList<T>不会将对象自动销毁。
受Symbian编程的影响,我习惯于对象自己创建、对象自己销毁,特别是在使用对象集合类,有时候一个对象可能在这个集合内,同时又在另外一个集合内。如果TObjectList<T>把对象销毁了,在另外一个集合内使用对象会造成不可预料的后果,所以建议大家不要使用TObjectList<T>。
其他几个泛型类TDictionary<>等,方法和代码都和.net的类似,看了看代码,真是让人遐想连篇,此处不再介绍。


昨晚下班的时候心血来潮,忽然想起我的“旧爱”Delphi前段时间被易博龙收购,现在不知道怎么样了,于是去网上搜索“Delphi2008”,想不到出来一大堆“Delphi2009”,令我兴奋不已,听说正式版20080825发出。下载了网友制作的简化pre-release版,赶紧体验了下,今天立马将心得提交上来与网友分享,这是必须的!
浙公网安备 33010602011771号