程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> Delphi2009初體驗 - 語言篇 - 體驗泛型(一)

Delphi2009初體驗 - 語言篇 - 體驗泛型(一)

編輯:Delphi

一、概述

二、體驗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>作對比,保存一組整形數據。使用控制台的方式進行程序編寫。

1program TestTList;
2
3{$APPTYPE CONSOLE}
4
5uses
6  SysUtils,
7  Classes,
8  Generics.Collections; // 泛型集合命名空間,太優美了!
9
10var
11  intList: TList<Integer>;
12  oldList: TList;
13  n, elem: Integer;
14  pTmp: Pointer;
15begin
16  // 以下代碼測試舊的集合對象
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 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('press any key');
58  Readln;
59end.

運行結果:

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

控制台程序代碼:

1program TestTObjectList;
2
3{$APPTYPE CONSOLE}
4
5uses
6  SysUtils,
7  Contnrs,
8  Generics.Collections,
9  Felix in 'Felix.pas';
10
11var
12  objList: TObjectList<TFelix>;
13  oldObjList: TObjectList;
14  n: Integer;
15  felix: TFelix;
16  pFelix: Pointer;
17begin
18  // 以下代碼測試舊對象集合
19  Writeln('TObjectList start');
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('press any key');
52  Readln;
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的類似,看了看代碼,真是讓人遐想連篇,此處不再介紹。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved