快速导航:
一、概述
二、体验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:
图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 />
-->1programTestTList; 2 3{$APPTYPECONSOLE} 4 5uses 6SysUtils, 7Classes, 8Generics.Collections;//泛型集合命名空间,太优美了! 9 10var 11intList:TList<Integer>; 12oldList:TList; 13n,elem:Integer; 14pTmp:Pointer; 15begin 16//以下代码测试旧的集合对象 17oldList:=TList.Create; 18oldList.Add(Pointer(1)); 19oldList.Add(Pointer(2)); 20oldList.Add(Pointer(3)); 21 22Writeln('TListstart'); 23 24forn:=0tooldList.Count-1do 25begin 26Writeln(Integer(oldList[n])); 27end; 28 29forpTmpinoldListdo 30begin 31Writeln(Integer(pTmp)); 32end; 33 34FreeAndNil(oldList); 35 36//以下代码测试整形泛型集合对象 37intList:=TList<Integer>.Create; 38intList.Add(1); 39intList.Add(2); 40intList.Add(3); 41 42Writeln(#13+#10+'TList<T>start'); 43 44forn:=0tointList.Count-1do 45begin 46Writeln(intList[n]); 47end; 48 49foreleminintListdo 50begin 51Writeln(elem); 52end; 53 54FreeAndNil(intList); 55 56//---------------------------------------------------------- 57Writeln('pressanykey'); 58Readln; 59end.
运行结果:
图2
三、体验TObjectList<T>
刚开始看到TObjectList的时候我有点不解,既然是泛型,那么T就不区分值类型和引用类型,为什么还会多出来一个TObjectList<
T>呢?在阅读了Generic.Collections的源码和经过试验后,我终于明白了原因,待我来慢慢分析。
同样,我将使用Contnrs命名空间下的TObjectList和TObjectList<T>做对比,使用控制台程序进行程序的编写。
首先创建一个类,该类在创建时,对象将打印“创建 ” + 对象的索引号,销毁时打印“销毁 ” + 对象的索引号:
1unitFelix; 2 3interface 4 5uses 6SysUtils; 7 8type 9TFelix=class 10private 11fId:Integer; 12public 13constructorCreate;virtual; 14destructorDestroy;override; 15propertyId:IntegerreadfIdwritefId; 16end; 17 18var 19gCount:Integer; 20 21implementation 22 23{TFelix} 24 25constructorTFelix.Create; 26begin 27fId:=gCount; 28Writeln('ConstructorFelix'+IntToStr(fId)); 29Inc(gCount); 30end; 31 32destructorTFelix.Destroy; 33begin 34Writeln('DestructorFelix'+IntToStr(fId)); 35 36inherited; 37end; 38 39end. 40
<!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->1unitFelix; 2 3interface 4 5uses 6SysUtils; 7 8type 9TFelix=class 10private 11fId:Integer; 12public 13constructorCreate;virtual; 14destructorDestroy;override; 15propertyId:IntegerreadfIdwritefId; 16end; 17 18var 19gCount:Integer; 20 21implementation 22 23{TFelix} 24 25constructorTFelix.Create; 26begin 27fId:=gCount; 28Writeln('ConstructorFelix'+IntToStr(fId)); 29Inc(gCount); 30end; 31 32destructorTFelix.Destroy; 33begin 34Writeln('DestructorFelix'+IntToStr(fId)); 35 36inherited; 37end; 38 39end.
控制台程序代码:
<!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->1programTestTObjectList; 2 3{$APPTYPECONSOLE} 4 5uses 6SysUtils, 7Contnrs, 8Generics.Collections, 9Felixin'Felix.pas'; 10 11var 12objList:TObjectList<TFelix>; 13oldObjList:TObjectList; 14n:Integer; 15felix:TFelix; 16pFelix:Pointer; 17begin 18//以下代码测试旧对象集合 19Writeln('TObjectListstart'); 20 21oldObjList:=TObjectList.Create;//1* 22forn:=0to2do 23begin 24oldObjList.Add(TFelix.Create); 25end; 26 27forpFelixinoldObjListdo 28begin 29Writeln(TFelix(pFelix).Id); 30end; 31 32FreeAndNil(oldObjList); 33 34//以下代码测试泛型对象集合 35Writeln(#13+#10+'TObjectList<T>start'); 36 37objList:=TObjectList<TFelix>.Create;//2* 38forn:=0to2do 39begin 40objList.Add(TFelix.Create); 41end; 42 43forfelixinobjListdo 44begin 45Writeln(felix.Id); 46end; 47 48FreeAndNil(objList); 49 50//---------------------------------------------------------- 51Writeln('pressanykey'); 52Readln; 53end.
图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的类似,看了看代码,真是让人遐想连篇,此处不再介绍。
|
请发表评论