Delphi2009初体验 - 语言篇 - 体验泛型(一)

系统 1624 0

快速导航:

一、 概述

二、 体验TList<T>

三、 体验TObjectList<T>

四、 TList<T>和TObjectList<T>的区别

五、 后记

一、概述

等了几百年, 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

Delphi2009初体验 - 语言篇 - 体验泛型(一)

1

为什么是“ [] ”,而不是“ () ”?

下面针对TList和TObjectList及两者的区别对Delphi2009的泛型功能进行初步体验。

二、体验TList<T>

在此,我将使用以前版本的指针集合类TList与TList<T>作对比,保存一组整形数据。使用控制台的方式进行程序编写。


<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --> 1 program TestTList;
2
3 { $APPTYPECONSOLE }
4
5 uses
6 SysUtils,
7 Classes,
8 Generics.Collections; // 泛型集合命名空间,太优美了!
9
10 var
11 intList:TList < Integer > ;
12 oldList:TList;
13 n,elem:Integer;
14 pTmp:Pointer;
15 begin
16 // 以下代码测试旧的集合对象
17 oldList: = TList.Create;
18 oldList.Add(Pointer( 1 ));
19 oldList.Add(Pointer( 2 ));
20 oldList.Add(Pointer( 3 ));
21
22 Writeln( ' TListstart ' );
23
24 for n: = 0 to oldList.Count - 1 do
25 begin
26 Writeln(Integer(oldList[n]));
27 end ;
28
29 for pTmp in oldList do
30 begin
31 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 do
45 begin
46 Writeln(intList[n]);
47 end ;
48
49 for elem in intList do
50 begin
51 Writeln(elem);
52 end ;
53
54 FreeAndNil(intList);
55
56 // ----------------------------------------------------------
57 Writeln( ' pressanykey ' );
58 Readln;
59 end .

运行结果:

Delphi2009初体验 - 语言篇 - 体验泛型(一)

图2

三、体验TObjectList<T>

刚开始看到TObjectList的时候我有点不解,既然是泛型,那么T就不区分值类型和引用类型,为什么还会多出来一个TObjectList< T>呢?在阅读了Generic.Collections的源码和经过试验后,我终于明白了原因,待我来慢慢分析。

同样,我将使用Contnrs命名空间下的TObjectList和TObjectList<T>做对比,使用控制台程序进行程序的编写。

首先创建一个类,该类在创建时,对象将打印“创建 ” + 对象的索引号,销毁时打印“销毁 ” + 对象的索引号:

1 unit Felix;
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( ' ConstructorFelix ' + IntToStr(fId));
29 Inc(gCount);
30 end ;
31
32 destructor TFelix.Destroy;
33 begin
34 Writeln( ' DestructorFelix ' + IntToStr(fId));
35
36 inherited ;
37 end ;
38
39 end .
40

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --> 1 unit Felix;
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( ' ConstructorFelix ' + IntToStr(fId));
29 Inc(gCount);
30 end ;
31
32 destructor TFelix.Destroy;
33 begin
34 Writeln( ' DestructorFelix ' + IntToStr(fId));
35
36 inherited ;
37 end ;
38
39 end .

控制台程序代码:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --> 1 program TestTObjectList;
2
3 { $APPTYPECONSOLE }
4
5 uses
6 SysUtils,
7 Contnrs,
8 Generics.Collections,
9 Felix in ' Felix.pas ' ;
10
11 var
12 objList:TObjectList < TFelix > ;
13 oldObjList:TObjectList;
14 n:Integer;
15 felix:TFelix;
16 pFelix:Pointer;
17 begin
18 // 以下代码测试旧对象集合
19 Writeln( ' TObjectListstart ' );
20
21 oldObjList: = TObjectList.Create; // 1 *
22 for n: = 0 to 2 do
23 begin
24 oldObjList.Add(TFelix.Create);
25 end ;
26
27 for pFelix in oldObjList do
28 begin
29 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 do
39 begin
40 objList.Add(TFelix.Create);
41 end ;
42
43 for felix in objList do
44 begin
45 Writeln(felix.Id);
46 end ;
47
48 FreeAndNil(objList);
49
50 // ----------------------------------------------------------
51 Writeln( ' pressanykey ' );
52 Readln;
53 end .

Delphi2009初体验 - 语言篇 - 体验泛型(一)

图3

如果我们将代码中的第1*处修改成:

oldObjList := TObjectList.Create(False);

将产生如下结果:


Delphi2009初体验 - 语言篇 - 体验泛型(一)

图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的类似,看了看代码,真是让人遐想连篇,此处不再介绍。

Delphi2009初体验 - 语言篇 - 体验泛型(一)


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论