小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

delphi Tstrings TCollection類(lèi)

 quasiceo 2013-01-19

delphi Tstrings 類(lèi)

(2010-05-13 14:50:19)


 

引用

xiaojinetdelphi Tstrings 類(lèi)

根據(jù)下標(biāo)存取列表中的字符串是最常見(jiàn)的一種操作,用法示意如下:

StringList1.Strings[0] := '字符串1';

注意在Delphi中,幾乎所有的列表的下標(biāo)都是以0為底的,也就是說(shuō)Strings[0]是列表中的第一個(gè)字符串。另外,由于Strings屬性是字符串列表類(lèi)的默認(rèn)屬性,因此可以省略Strings,直接用下面的簡(jiǎn)便方法存取字符串:

StringList1[0] := '字符串1';



定位一個(gè)列表中特定的字符串的位置,可以使用IndexOf方法,IndexOf方法將會(huì)返回在字符串列表中的第一個(gè)匹配的字符串的索引值,如果沒(méi)有匹配的字符串則返回-1。比如我們可以使用IndexOf方法來(lái)察看特定文件是否存在于文件列表框中,代碼示意如下:



if FileListBox1.Items.IndexOf('TargetFileName') > -1 ...



有一點(diǎn)不方便的是TStrings類(lèi)沒(méi)有提供一個(gè)方法可以查找除了第一個(gè)匹配字符串外其他同樣匹配的字符串的索引,只能是自己遍歷字符串列表來(lái)實(shí)現(xiàn),這點(diǎn)不如C++中的模版容器類(lèi)以及相關(guān)的模版算法強(qiáng)大和方便。下面是一個(gè)遍歷字符串列表的示意,代碼遍歷列表框中的所有字符串,并將其全部轉(zhuǎn)化為大寫(xiě)的字符串:



procedure TForm1.Button1Click(Sender: TObject);var Index: Integer;

begin

for Index := 0 to ListBox1.Items.Count - 1 do

ListBox1.Items[Index] := UpperCase(ListBox1.Items[Index]);

end;



前面我們看到了,要想向字符串列表中添加字符串,直接使用Add方法就可以了,但是Add方法只能將字符串加入到列表的末尾,要想在列表的指定位置添加字符串,需要使用Insert方法,下面代碼在列表的索引為2的位置添加了字符串:



StringList1.Insert(2, 'Three');



如果要想將一個(gè)字符串列表中的所有字符串都添加到另一個(gè)字符串列表中,可以使用AddStrings方法,用法如下:

StringList1.AddStrings(StringList2);



要想克隆一個(gè)字符串列表的所有內(nèi)容,可以使用Assign方法,例如下面的方法將Combox1中的字符串列表復(fù)制到了Memo1中:

Memo1.Lines.Assign(ComboBox1.Items);

要注意的是使用了Assign方法后,目標(biāo)字符串列表中原有的字符串會(huì)全部丟失。



同對(duì)象關(guān)聯(lián)



前面說(shuō)了我們可以將字符串同對(duì)象綁定起來(lái),我們可以使用AddObject或者InsertObject方法向列表添加同字符串關(guān)聯(lián)的對(duì)象,也可以通過(guò)Objects屬性直接將對(duì)象同特定位置的字符串關(guān)聯(lián)。此外TStrings類(lèi)還提供了IndexOfObject方法返回指定對(duì)象的索引,同樣的Delete,Clear和Move等方法也可以作用于對(duì)象。不過(guò)要注意的是我們不能向字符串中添加一個(gè)沒(méi)有同字符串關(guān)聯(lián)的對(duì)象。



同視圖交互



剛剛學(xué)習(xí)使用Delphi的人都會(huì)為Delphi IDE的強(qiáng)大的界面交互設(shè)計(jì)功能所震驚,比如我們?cè)诖绑w上放上一個(gè)ListBox,然后在object Inspector中雙擊它的Items屬性(TStrings類(lèi)型),在彈出的對(duì)話(huà)框中,見(jiàn)下圖,我們輸入一些字符串后,點(diǎn)擊確定,關(guān)閉對(duì)話(huà)框,就會(huì)看到窗體上的ListBox中出現(xiàn)了我們剛才輸入的字符串。



可以我們?cè)赥Strings和默認(rèn)的實(shí)現(xiàn)類(lèi)TStringList的源代碼中卻找不到同ListBox相關(guān)的代碼,那么這種界面交互是如何做到的呢?



秘密就在于TListBox的Items屬性類(lèi)型實(shí)際上是TStrings的基類(lèi)TListBoxStrings類(lèi),我們看一下這個(gè)類(lèi)的定義:



TListBoxStrings = class(TStrings)

private

ListBox: TCustomListBox;

protected



public

function Add(const S: string): Integer; override;

procedure Clear; override;

procedure Delete(Index: Integer); override;

procedure Exchange(Index1, Index2: Integer); override;

function IndexOf(const S: string): Integer; override;

procedure Insert(Index: Integer; const S: string); override;

procedure Move(CurIndex, NewIndex: Integer); override;

end;

可以看到TListBoxStrings類(lèi)實(shí)現(xiàn)了TStrings類(lèi)的所有抽象方法,同時(shí)在內(nèi)部有一個(gè)ListBox的私有變量。我們?cè)倏匆幌耇ListBoxStrings的Add方法:

function TListBoxStrings.Add(const S: string): Integer;
begin
Result := -1;
if ListBox.Style in [lbVirtual, lbVirtualOwnerDraw] then exit;
Result := SendMessage(ListBox.Handle, LB_ADDSTRING, 0, Longint(PChar(S)));
if Result < 0 then raise EOutOfResources.Create(SInsertLineError);
end;


可以看到TListBoxStrings在內(nèi)部并沒(méi)有保存添加的字符串,而是直接向Windows的原生列表盒控件發(fā)送消息實(shí)現(xiàn)的代碼添加,而Windows的原生列表盒是一個(gè)MVC的組件,當(dāng)內(nèi)部的數(shù)據(jù)發(fā)生變化時(shí),會(huì)自動(dòng)改變視圖顯示,這就是為什么我們?cè)谠O(shè)計(jì)器中輸入的字符串會(huì)立刻顯示在窗體列表框中的原因了。



于是我們也就知道為什么Borland將TStrings設(shè)計(jì)為一個(gè)抽象的類(lèi)而沒(méi)有提供一個(gè)默認(rèn)的存儲(chǔ)方式,就是因?yàn)楹芏嗟慕缑娼M件在內(nèi)部對(duì)數(shù)據(jù)的存儲(chǔ)有很多不同的方式,Borland決定針對(duì)不同的組件提供不同的存儲(chǔ)和交互方式。同樣的我們要編寫(xiě)的組件如果有TStrings類(lèi)型的屬性,同時(shí)也要同界面或者其它資源交互的話(huà),不要使用TStringList來(lái)實(shí)現(xiàn),而應(yīng)該從TStrings派生出新類(lèi)來(lái)實(shí)現(xiàn)更好的交互設(shè)計(jì)。



還有一點(diǎn)要說(shuō)明的是,Delphi的IDE只在使用Delphi的流機(jī)制保存組件到窗體設(shè)計(jì)文件DFM文件中的時(shí),做了一些特殊的處理,能夠自動(dòng)保存和加載Published的TStrings類(lèi)型的屬性,下面就是一個(gè)ListBox儲(chǔ)存在窗體設(shè)計(jì)文件DFM中文本形式示意(在窗體設(shè)計(jì)階段,我們可以直接使用View As Text右鍵菜單命令看到下面的文本),我們可以注意到在設(shè)計(jì)時(shí)我們輸入的Items的兩個(gè)字符串被保存了起來(lái):



object ListBox1: TListBox

Left = 64

Top = 40

Width = 145

Height = 73

ItemHeight = 16

Items.Strings = (

'String1'

'String2')

TabOrder = 1

end

隨后如果運(yùn)行程序時(shí),VCL庫(kù)會(huì)使用流從編譯進(jìn)可執(zhí)行文件的DFM資源中將Items.Strings列表加載到界面上,這樣就實(shí)現(xiàn)了設(shè)計(jì)是什么樣,運(yùn)行時(shí)也是什么樣的所見(jiàn)即所得。



鍵-值對(duì)



在實(shí)際開(kāi)發(fā)過(guò)程中,我們經(jīng)常會(huì)碰到類(lèi)似于字典的定位操作的通過(guò)鍵查找相應(yīng)值的操作,比如通過(guò)用戶(hù)名查找用戶(hù)相應(yīng)的登陸密碼等。在C++和Java中,標(biāo)準(zhǔn)模版庫(kù)和JDK都提供了Map類(lèi)來(lái)實(shí)現(xiàn)鍵-值機(jī)制,但是Delphi的VCL庫(kù)卻沒(méi)有提供這樣的類(lèi),但是TStrings類(lèi)提供了一個(gè)簡(jiǎn)易的Map替代的實(shí)現(xiàn),那就是Name-Value對(duì)。



對(duì)于TStrings來(lái)說(shuō),所謂的Name-Value對(duì),實(shí)際上就是’Key=Value’這樣包含=號(hào)的分割的字符串,等號(hào)左邊的部分就是Name,等號(hào)右邊的部分就是Value。TStrings類(lèi)提供了IndexOfName和Values等屬性方法來(lái)操作Name-Value對(duì)。下面是用法示意:



var

StringList1:TStrings;

Begin

StringList1:=TStringList.Create;

//添加用戶(hù)名-密碼對(duì)

StringList1.Add(‘hubdog=aaa’);

StringList1.Add(‘hubcat=bbb’);

….

//根據(jù)用戶(hù)名hubdog查找密碼

Showmessage(StringList1.Values[StringList1.IndexOfName(‘hubdog’)]);

End;



從Delphi7開(kāi)始,TStrings類(lèi)增加了一個(gè)NameValueSeparator屬性,我們可以通過(guò)這個(gè)屬性修改默認(rèn)的Name-Value分割符號(hào)為=號(hào)以外的其它符號(hào)了。還要說(shuō)明的是,TStrings的Name-Value對(duì)中的Name可以不唯一,這有點(diǎn)類(lèi)似于C++中的MultiMap,這時(shí)通過(guò)Values[Names[IndexOfName]]下標(biāo)操作取到的值不一定是我們所需要的,另外TStrings類(lèi)的Name-Value對(duì)的查找定位是采用的遍歷的方式,而不同于Java和C++中的Map是基于哈希表或者樹(shù)的實(shí)現(xiàn),因此查找和定位的效率非常低,不適用于性能要求非常高的場(chǎng)景。不過(guò)從Delphi6開(kāi)始,VCL庫(kù)中在IniFiles單元中提供了一個(gè)基于哈希表的字符串列表類(lèi)THashedStringList類(lèi)可以極大的提高查找定位的速度。



THashedStringList類(lèi)



一般來(lái)說(shuō),通過(guò)鍵來(lái)查找值最簡(jiǎn)單的辦法是遍歷列表對(duì)列表中的鍵進(jìn)行比較,如果相等則獲取相應(yīng)的鍵值。但是這種簡(jiǎn)單的辦法也是效率最差的一種辦法,當(dāng)列表中的項(xiàng)目比較少時(shí),這種辦法還可以接受,但是如果列表中項(xiàng)目非常多的話(huà),這種方法會(huì)極大的影響軟件的運(yùn)行速度。 這時(shí)我們可以使用哈希表來(lái)快速的通過(guò)鍵值來(lái)存取列表中的元素。由于本書(shū)并不是一本數(shù)據(jù)結(jié)構(gòu)和算法的書(shū),因此我無(wú)意在這里討論哈希表背后的理論知識(shí),我們只要知道哈??梢酝ㄟ^(guò)鍵快速定位相應(yīng)的值就可以了,對(duì)此感興趣的非計(jì)算機(jī)專(zhuān)業(yè)的人可以去察看相關(guān)的書(shū),這里就不贅述了。



Delphi6中提供的THashedStringList類(lèi)沒(méi)有提供任何的新的方法,只是對(duì)IndexOf和IndexOfName函數(shù)通過(guò)哈希表進(jìn)行了性能優(yōu)化,下面這個(gè)例子演示了TStringList和THashedStringList之間的性能差異:


unit CHash;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Inifiles;

type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
HashedList: THashedStringList;
DesList: TStringList;
List: TStringList;
public
{ Public declarations }
procedure Hash;
procedure Iterate;
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
I:Integer;
begin
Screen.Cursor := crHourGlass;
try
//初始化系統(tǒng)
for I := 0 to 5000 do
begin
HashedList.Add(IntToStr(i));
List.Add(IntToStr(i));
end;
Hash;
DesList.Clear;
Iterate;
finally
Screen.Cursor := crDefault;
end;
end;

procedure TForm1.Hash;
var
I, J: Integer;
begin
//基于哈希表的定位
for I := 3000 to 4000 do
begin
DesList.Add(IntToStr(HashedList.IndexOf(IntToStr(I))));
end;
end;

procedure TForm1.Iterate;
var
I, J: Integer;
begin
//基于遍歷方式定位
for I := 3000 to 4000 do
begin
DesList.Add(IntToStr(List.IndexOf(IntToStr(I))));
end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
HashedList := THashedStringList.Create;
DesList := TStringList.Create;
List := TStringList.Create;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
HashedList.Free;
DesList.Free;
List.Free;
end;

end.

上面代碼中的Hash過(guò)程,采用了新的THashedStringList類(lèi)來(lái)實(shí)現(xiàn)的查找,而Iterate過(guò)程中使用了原來(lái)的TStringList類(lèi)的IndexOfName來(lái)實(shí)現(xiàn)的查找。采用GpProfile(注:GpProfile的用法參見(jiàn)工具篇的性能分析工具GpProfile章節(jié))對(duì)兩個(gè)過(guò)程進(jìn)行了性能比較后,從下圖可以看到Hash執(zhí)行同樣查找動(dòng)作只用了0.7%的時(shí)間,而Iterate方法則用了99.3%的時(shí)間,可以看到在字符串列表項(xiàng)目數(shù)在幾千的數(shù)量級(jí)別時(shí),基于哈希表的查詢(xún)速度是原有方法的100多倍。



不過(guò)要說(shuō)明的是,THashedStringList同TStringList類(lèi)相比,雖然查找的速度大大提高了,但是在添加、刪除字符串后再次進(jìn)行查找操作時(shí),需要重新計(jì)算哈希函數(shù),所以如果頻繁的進(jìn)行刪除或者添加同查找的復(fù)合操作,執(zhí)行的速度很有可能比TStringList還要慢,這是使用時(shí)需要注意的。



TBucketList和TObjectBucketList類(lèi)



從Delphi6開(kāi)始,VCL的Contnrs單元中又增加了兩個(gè)新的容器類(lèi)TBucketList和TObjectBucketList。TBucketList實(shí)際上也是一個(gè)簡(jiǎn)單基于哈希表的指針-指針對(duì)列表。接口定義如下:



TBucketList = class(TCustomBucketList)



public

destructor Destroy; override;

procedure Clear;

function Add(AItem, AData: Pointer): Pointer;

function Remove(AItem: Pointer): Pointer;

function ForEach(AProc: TBucketProc; AInfo: Pointer = nil): Boolean;

procedure Assign(AList: TCustomBucketList);

function Exists(AItem: Pointer): Boolean;

function Find(AItem: Pointer; out AData: Pointer): Boolean;

property Data[AItem: Pointer]: Pointer read GetData write SetData; default;

end;



類(lèi)的Add方法現(xiàn)在接受兩個(gè)參數(shù)AItem和AData,我們可以把它看成是指針版的Map實(shí)現(xiàn)(從容器類(lèi)來(lái)看, Delphi從語(yǔ)言的靈活性來(lái)說(shuō)不如C++,為了實(shí)現(xiàn)不同類(lèi)型的哈希Map容器,Delphi需要派生很多的類(lèi),而C++的Map是基于模版技術(shù)來(lái)實(shí)現(xiàn)的,容器元素的類(lèi)型只要簡(jiǎn)單的聲明一下就能指定了,使用起來(lái)非常方便。而從簡(jiǎn)單性來(lái)說(shuō),則不如Java的容器類(lèi),因?yàn)镈elphi中的String是原生類(lèi)型,而不是類(lèi),并且Delphi還提供對(duì)指針的支持,因此要為指針和字符串提供不同的Map派生類(lèi)),類(lèi)中的Exists和Find等方法都是通過(guò)哈希表來(lái)實(shí)現(xiàn)快速數(shù)據(jù)定位的。同時(shí),同一般的列表容器類(lèi)不同,TBucketList不提供通過(guò)整數(shù)下標(biāo)獲取列表中的元素的功能,不過(guò)我們可以使用ForEach方法來(lái)遍歷容器內(nèi)的元素。



TObjectBucketList是從TBucketList派生的基類(lèi),沒(méi)有增加任何新的功能,唯一的不同之處就是容器內(nèi)的元素不是指針而是對(duì)象了,實(shí)現(xiàn)了更強(qiáng)的類(lèi)型檢查而已。



其它容器類(lèi)



TThreadList類(lèi)



TThreadList類(lèi)實(shí)際上就是一個(gè)線(xiàn)程安全的TList類(lèi),每次添加或者刪除容易中指針時(shí),TThreadList會(huì)調(diào)用EnterCriticalSection函數(shù)進(jìn)入線(xiàn)程阻塞狀態(tài),這時(shí)其它后續(xù)發(fā)生的對(duì)列表的操作都會(huì)阻塞在那里,直到TThreadList調(diào)用UnLockList釋放對(duì)列表的控制后才會(huì)被依次執(zhí)行。在多線(xiàn)程開(kāi)發(fā)中,我們需要使用TThreadList來(lái)保存共享的資源以避免多線(xiàn)程造成的混亂和沖突。還要注意的是TThreadList有一個(gè)Duplicates布爾屬性,默認(rèn)為T(mén)rue,表示列表中不能有重復(fù)的指針。設(shè)定為False將允許容器內(nèi)有重復(fù)的元素。



TInterfaceList類(lèi)



在Classes單元中,VCL還定義了一個(gè)可以保存接口的列表類(lèi)。我們可以向列表中添加接口類(lèi)型,這個(gè)類(lèi)的操作方法同其它的列表類(lèi)沒(méi)有什么區(qū)別,只不過(guò)在內(nèi)部使用TThreadList作為容器實(shí)現(xiàn)了線(xiàn)程安全。



擬容器類(lèi)TBits類(lèi)



在Classes.pas還有一個(gè)特殊的TBits類(lèi),接口定義如下:

TBits = class



public

destructor Destroy; override;

function OpenBit: Integer;

property Bits[Index: Integer]: Boolean read GetBit write SetBit; default;

property Size: Integer read FSize write SetSize;

end;



它可以按位儲(chǔ)存布爾值,因此可以看成是一個(gè)原生的Boolean值的容器類(lèi),但是它缺少列表類(lèi)的很多方法和特性,不能算是一個(gè)完整的容器,因此我們稱(chēng)它為擬容器類(lèi)。



在我們開(kāi)發(fā)過(guò)程中,經(jīng)常需要表示一些類(lèi)似于開(kāi)關(guān)的二元狀態(tài),這時(shí)我們用TBits來(lái)表示一組二元狀態(tài)非常方便,同時(shí)TBits類(lèi)的成員函數(shù)主要是用匯編語(yǔ)言寫(xiě)的,位操作的速度非???。二元狀態(tài)組的大小通過(guò)設(shè)定TBits類(lèi)的Size屬性來(lái)動(dòng)態(tài)的調(diào)整,存取Boolean值可以通過(guò)下標(biāo)來(lái)存取TBits類(lèi)的Bits屬性來(lái)實(shí)現(xiàn)。至于OpenBit函數(shù),它返回第一個(gè)不為T(mén)rue的Boolean值的下標(biāo)。從接口定義可以看出,TBits類(lèi)接口非常簡(jiǎn)單,提供的功能也很有限,我猜測(cè)這只是Borland的研發(fā)隊(duì)伍滿(mǎn)足內(nèi)部開(kāi)發(fā)有限需要的類(lèi),并不是作為一個(gè)通用類(lèi)來(lái)設(shè)計(jì)的,比如它沒(méi)有開(kāi)放內(nèi)部數(shù)據(jù)存取的接口,無(wú)法獲得內(nèi)部數(shù)據(jù)的表達(dá),進(jìn)而無(wú)法實(shí)現(xiàn)對(duì)狀態(tài)的保存和加載等更高的需求。



TCollection類(lèi)



前面我們提到了Delphi的IDE能夠自動(dòng)將字符串列表保存在DFM文件中,并能在運(yùn)行時(shí)將設(shè)計(jì)期編輯的字符串列表加載進(jìn)內(nèi)存(也就是我們通常所說(shuō)的類(lèi)的可持續(xù)性)。TStrings這種特性比較適合于保存一個(gè)對(duì)象同多個(gè)字符串?dāng)?shù)據(jù)之間關(guān)聯(lián),比較類(lèi)似于現(xiàn)實(shí)生活中一個(gè)人同多個(gè)Email賬戶(hù)地址之間的關(guān)系。但是,TStrings類(lèi)型的屬性有一個(gè)很大的局限那就是,它只能用于設(shè)計(jì)時(shí)保存簡(jiǎn)單的字符串列表,而不能保存復(fù)雜對(duì)象列表。而一個(gè)父對(duì)象同多個(gè)子對(duì)象之間的聚合關(guān)系可能更為常見(jiàn),比如一列火車(chē)可能有好多節(jié)車(chē)廂構(gòu)成,每節(jié)車(chē)廂都有車(chē)廂號(hào),車(chē)廂類(lèi)型(臥鋪,還是硬座),車(chē)廂座位數(shù),車(chē)廂服務(wù)員名稱(chēng)等屬性構(gòu)成。如果我們想在設(shè)計(jì)期實(shí)現(xiàn)對(duì)火車(chē)的車(chē)廂定制的功能,并能保存車(chē)廂的各個(gè)屬性到窗體文件中,則車(chē)廂集合屬性定義為T(mén)Strings類(lèi)型的屬性是行不通的。



對(duì)于這個(gè)問(wèn)題,Delphi提供了TCollection容器類(lèi)屬性這樣一個(gè)解決方案。TCollection以及它的容器元素TCollectionItem的接口定義如下:

TCollection = class(TPersistent)

protected
procedure Added(var Item: TCollectionItem); virtual; deprecated;
procedure Deleting(Item: TCollectionItem); virtual; deprecated;
property NextID: Integer read FNextID;
procedure Notify(Item: TCollectionItem; Action: TCollectionNotification); virtual;
{ Design-time editor support }
function GetAttrCount: Integer; dynamic;
function GetAttr(Index: Integer): string; dynamic;
function GetItemAttr(Index, ItemIndex: Integer): string; dynamic;
procedure Changed;
function GetItem(Index: Integer): TCollectionItem;
procedure SetItem(Index: Integer; Value: TCollectionItem);
procedure SetItemName(Item: TCollectionItem); virtual;
procedure Update(Item: TCollectionItem); virtual;
property PropName: string read GetPropName write FPropName;
property UpdateCount: Integer read FUpdateCount;
public
constructor Create(ItemClass: TCollectionItemClass);
destructor Destroy; override;
function Owner: TPersistent;
function Add: TCollectionItem;
procedure Assign(Source: TPersistent); override;
procedure BeginUpdate; virtual;
procedure Clear;
procedure Delete(Index: Integer);
procedure EndUpdate; virtual;
function FindItemID(ID: Integer): TCollectionItem;
function GetNamePath: string; override;
function Insert(Index: Integer): TCollectionItem;
property Count: Integer read GetCount;
property ItemClass: TCollectionItemClass read FItemClass;
property Items[Index: Integer]: TCollectionItem read GetItem write SetItem;
end;


TCollectionItem = class(TPersistent)

protected
procedure Changed(AllItems: Boolean);
function GetOwner: TPersistent; override;
function GetDisplayName: string; virtual;
procedure SetCollection(Value: TCollection); virtual;
procedure SetIndex(Value: Integer); virtual;
procedure SetDisplayName(const Value: string); virtual;
public
constructor Create(Collection: TCollection); virtual;
destructor Destroy; override;
function GetNamePath: string; override;
property Collection: TCollection read FCollection write SetCollection;
property ID: Integer read FID;
property Index: Integer read GetIndex write SetIndex;
property DisplayName: string read GetDisplayName write SetDisplayName;
end;


TCollection類(lèi)是一個(gè)比較復(fù)雜特殊的容器類(lèi)。但是初看上去,它就是一個(gè)TCollectionItem對(duì)象的容器類(lèi),同列表類(lèi)TList類(lèi)似,TCollection類(lèi)也維護(hù)一個(gè)TCollectionItem對(duì)象索引數(shù)組,Count屬性表示容器中包含的TCollectionItem的數(shù)目,同時(shí)也提供了Add和Delete方法來(lái)添加和刪除TCollectionItem對(duì)象以及通過(guò)下標(biāo)存取TCollectionItem的屬性??瓷先ズ腿萜黝?lèi)區(qū)別不大,但是在VCL內(nèi)部用于保存和加載組件的TReader和TWriter類(lèi)提供了兩個(gè)特殊的方法WriteCollection和ReadCollection用于加載和保存TCollection類(lèi)型的集合屬性。IDE就是通過(guò)這兩個(gè)方法實(shí)現(xiàn)對(duì)TCollection類(lèi)型屬性的可持續(xù)性。



假設(shè)現(xiàn)在需要設(shè)計(jì)一個(gè)火車(chē)組件TTrain,TTrain組件有一個(gè)TCollection類(lèi)型的屬性Carriages表示多節(jié)車(chē)廂構(gòu)成的集合屬性,每個(gè)車(chē)廂則對(duì)應(yīng)于集合屬性的元素,從TCollectionItem類(lèi)繼承,有車(chē)廂號(hào),車(chē)廂類(lèi)型(臥鋪,還是硬座),車(chē)廂座位數(shù),車(chē)廂服務(wù)員名稱(chēng)等屬性,下面是我設(shè)計(jì)的組件的接口:



type
//車(chē)廂類(lèi)型,硬座、臥鋪
TCarriageType = (ctHard, ctSleeper);
//車(chē)廂類(lèi)
TCarriageCollectionItem = class(TCollectionItem)

published
//車(chē)廂號(hào)碼
property CarriageNum: Integer read FCarriageNum write FCarriageNum;
//座位數(shù)
property SeatCount: Integer read FSeatCount write FSeatCount;
//車(chē)廂類(lèi)型
property CarriageType: TCarriageType read FCarriageType write FCarriageType;
//服務(wù)員名稱(chēng)
property ServerName: string read FServerName write FServerName;
end;

TTrain=class;
//車(chē)廂容器屬性類(lèi)
TCarriageCollection = class(TCollection)
private
FTrain:TTrain;
function GetItem(Index: Integer): TCarriageCollectionItem;
procedure SetItem(Index: Integer; const Value: TCarriageCollectionItem);
protected
function GetOwner: TPersistent; override;
public
constructor Create(ATrain: TTrain);
function Add: TCarriageCollectionItem;
property Items[Index: Integer]: TCarriageCollectionItem read GetItem
write SetItem; default;
end;

//火車(chē)類(lèi)
TTrain = class(TComponent)
private
FItems: TCarriageCollection;
procedure SetItems(Value: TCarriageCollection);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property Carriages: TCarriageCollection read FItems write SetItems;
end;


其中車(chē)廂類(lèi)的定義非常簡(jiǎn)單,只是定義了四個(gè)屬性。而車(chē)廂集合類(lèi)重定義了靜態(tài)的Add方法以及Items屬性,其返回結(jié)果類(lèi)型改為了TCarriageCollectionItem,下面是車(chē)廂集合類(lèi)的實(shí)現(xiàn)代碼:



function TCarriageCollection.Add: TCarriageCollectionItem;

begin

Result:=TCarriageCollectionItem(inherited Add);

end;



constructor TCarriageCollection.Create(ATrain: TTrain);

begin

inherited Create(TCarriageCollectionItem);

FTrain:=ATrain;

end;



function TCarriageCollection.GetItem(

Index: Integer): TCarriageCollectionItem;

begin

Result := TCarriageCollectionItem(inherited GetItem(Index));

end;



function TCarriageCollection.GetOwner: TPersistent;

begin

Result:=FTrain;

end;



procedure TCarriageCollection.SetItem(Index: Integer;

const Value: TCarriageCollectionItem);

begin

inherited SetItem(Index, Value);

end;



其中Add,GetItem和SetItem都非常簡(jiǎn)單,就是調(diào)用基類(lèi)的方法,然后將基類(lèi)的方法的返回結(jié)果重新映射為T(mén)CollectionItem類(lèi)型。而構(gòu)造函數(shù)中將TTrain組件作為父組件傳入,并重載GetOwner方法,返回TTrain組件,這樣處理的原因是IDE會(huì)在保存集合屬性時(shí)調(diào)用集合類(lèi)的GetOwner確認(rèn)屬性的父控件是誰(shuí),這樣才能把集合屬性寫(xiě)到DFM文件中時(shí),才能存放到正確的位置下面,建立正確的聚合關(guān)系。



而火車(chē)組件的實(shí)現(xiàn)也非常簡(jiǎn)單,只要定義一個(gè)Published Carriages屬性就可以了,方法實(shí)現(xiàn)代碼如下:



constructor TTrain.Create(AOwner: TComponent);

begin

inherited;

FItems := TCarriageCollection.Create(Self);

end;



destructor TTrain.Destroy;

begin

FItems.Free;

inherited;

end;



procedure TTrain.SetItems(Value: TCarriageCollection);

begin

FItems.Assign(Value);

end;



下面將我們的組件注冊(cè)到系統(tǒng)面板上之后,就可以在窗體上放上一個(gè)TTrain組件,然后然后選中Object Inspector,然后雙擊Carriages屬性,會(huì)顯示系統(tǒng)默認(rèn)的集合屬性編輯器,使用Add按鈕向列表中添加兩個(gè)車(chē)廂,修改一下屬性,如下圖所示意:







從上面的屬性編輯器我們,可以看到默認(rèn)情況下,屬性編輯器列表框是按項(xiàng)目索引加上一個(gè)橫杠來(lái)顯示車(chē)廂的名稱(chēng),看起來(lái)不是很自然。要想修改顯示字符串,需要重載TCarriageCollectionItem的GetDisplayName方法。修改后的GetDisplayName方法顯示車(chē)廂加車(chē)廂號(hào)碼:



function TCarriageCollectionItem.GetDisplayName: string;

begin

Result:='車(chē)廂'+IntToStr(CarriageNum);

end;



示意圖:





保存一下文件,使用View As Text右鍵菜單命令察看一下DFM文件,我們會(huì)看到我們?cè)O(shè)計(jì)的車(chē)廂類(lèi)的屬性確實(shí)都被寫(xiě)到了DFM文件中,并且Carriages屬性的父親就是Train1:



object Train1: TTrain

Carriages = <

item

CarriageNum = 1

SeatCount = 100

CarriageType = ctHard

ServerName = '陳省'

end

item

CarriageNum = 2

SeatCount = 200

CarriageType = ctHard

ServerName = 'hubdog'

end>

Left = 16

Top = 8

End



TOwnedCollection

從Delphi4開(kāi)始,VCL增加了一個(gè)TOwnedCollection類(lèi),它是TCollection類(lèi)的子類(lèi),如果我們的TCarriageCollection類(lèi)是從TOwnedCollection類(lèi)繼承的,這時(shí)我們就不再需要向上面重載GetOwner方法并返回父控件給IDE,以便TCarriageCollection屬性能出現(xiàn)在Object Inspector中了。


總結(jié)


本章中我介紹了幾乎所有VCL中重要的容器類(lèi),其中TList及其子類(lèi)相當(dāng)于通用的容器類(lèi),雖然不如C++和Java功能那么強(qiáng)大,但是用好了已經(jīng)足以滿(mǎn)足我們90%的開(kāi)發(fā)需要,而TStrings及其子類(lèi),還有TCollection則是實(shí)現(xiàn)所見(jiàn)即所得設(shè)計(jì)的關(guān)鍵類(lèi),對(duì)于開(kāi)發(fā)靈活強(qiáng)大的自定義組件來(lái)說(shuō)是必不可少的

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀(guān)點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多