快速导航:

一、概述

二、体验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>作对比,保存一组整形数据。使用控制台的方式进行程序编写。

 

 1(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)program TestTList;
 2(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)
 3(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一){$APPTYPE CONSOLE}
 4(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)
 5(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)uses
 6(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    SysUtils,
 7(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    Classes,
 8(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    Generics.Collections; // 泛型集合命名空间,太优美了!
 9(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)
10(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)var
11(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    intList: TList<Integer>;
12(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    oldList: TList;
13(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    n, elem: Integer;
14(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    pTmp: Pointer;
15(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)begin
16(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    // 以下代码测试旧的集合对象
17(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    oldList := TList.Create;
18(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    oldList.Add(Pointer(1));
19(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    oldList.Add(Pointer(2));
20(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    oldList.Add(Pointer(3));
21(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)
22(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    Writeln('TList start');
23(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)
24(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    for n := 0 to oldList.Count - 1 do
25(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    begin
26(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)        Writeln(Integer(oldList[n]));
27(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    end;
28(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)
29(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    for pTmp in oldList do
30(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    begin
31(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)        Writeln(Integer(pTmp));
32(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    end;
33(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)
34(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    FreeAndNil(oldList);
35(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)
36(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    // 以下代码测试整形泛型集合对象
37(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    intList := TList<Integer>.Create;
38(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    intList.Add(1);
39(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    intList.Add(2);
40(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    intList.Add(3);
41(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)
42(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    Writeln( #13 + #10 + 'TList<T> start');
43(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)
44(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    for n := 0 to intList.Count - 1 do
45(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    begin
46(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)        Writeln(intList[n]);
47(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    end;
48(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)
49(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    for elem in intList do
50(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    begin
51(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)        Writeln(elem);
52(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    end;
53(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)
54(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    FreeAndNil(intList);
55(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)
56(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    // ----------------------------------------------------------
57(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    Writeln('press any key(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)');
58(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    Readln;
59(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)end.

 

运行结果:

 

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

图2

三、体验TObjectList<T>

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

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

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

 40 

 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('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.

 

 控制台程序代码:

 

 1(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)program TestTObjectList;
 2(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)
 3(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一){$APPTYPE CONSOLE}
 4(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)
 5(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)uses
 6(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    SysUtils,
 7(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    Contnrs,
 8(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    Generics.Collections,
 9(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    Felix in 'Felix.pas';
10(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)
11(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)var
12(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    objList: TObjectList<TFelix>;
13(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    oldObjList: TObjectList;
14(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    n: Integer;
15(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    felix: TFelix;
16(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    pFelix: Pointer;
17(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)begin
18(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    // 以下代码测试旧对象集合
19(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    Writeln('TObjectList start');
20(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)
21(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    oldObjList := TObjectList.Create;  // 1*
22(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    for n := 0 to 2 do
23(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    begin
24(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)        oldObjList.Add(TFelix.Create);
25(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    end;
26(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)
27(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    for pFelix in oldObjList do
28(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    begin
29(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)        Writeln(TFelix(pFelix).Id);
30(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    end;
31(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)
32(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    FreeAndNil(oldObjList);
33(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)
34(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    // 以下代码测试泛型对象集合
35(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    Writeln(#13 + #10 + 'TObjectList<T> start');
36(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)
37(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    objList := TObjectList<TFelix>.Create;  // 2*
38(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    for n := 0 to 2 do
39(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    begin
40(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)        objList.Add(TFelix.Create);
41(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    end;
42(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)
43(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    for felix in objList do
44(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    begin
45(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)        Writeln(felix.Id);
46(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    end;
47(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)
48(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    FreeAndNil(objList);
49(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)
50(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    // ----------------------------------------------------------
51(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    Writeln('press any key(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)');
52(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)    Readln;
53(原创)Delphi2009初体验 - 语言篇 - 体验泛型(一)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的类似,看了看代码,真是让人遐想连篇,此处不再介绍。

 

相关文章: