通過(guò)C#實(shí)現(xiàn)集合類(lèi)縱覽.NET Collections及相關(guān)技術(shù)
2024-07-10 13:04:33
供稿:網(wǎng)友
 
概述:在真正的對(duì)象化開(kāi)發(fā)項(xiàng)目中,我們通常會(huì)將常用的業(yè)務(wù)實(shí)體抽象為特定的類(lèi),如employee、customer、contact等,而多數(shù)的類(lèi)之間會(huì)存在著相應(yīng)的關(guān)聯(lián)或依存關(guān)系,如employee和customer通過(guò)contact而產(chǎn)生關(guān)聯(lián)、contact是依賴于employee和customer而存在的。在實(shí)際的對(duì)象應(yīng)用模塊中,可能會(huì)有這樣的需求:獲得一組客戶對(duì)象(即customers集合類(lèi)的實(shí)例,如customers),指向其中一個(gè)customer對(duì)象(如customers[i]),通過(guò)訪問(wèn)這個(gè)customer對(duì)象的屬性name(customers[i].name)和contacts(如customers[i].contacts)來(lái)查詢客戶的姓名和與該客戶的聯(lián)絡(luò)記錄,甚至遍歷contacts對(duì)象,查找該客戶的某次聯(lián)絡(luò)摘要(即customers.[i].contacts[x].summary)。為滿足以上集合類(lèi)的需求,對(duì)照.net framework 的平臺(tái)實(shí)現(xiàn),不難發(fā)現(xiàn).net在collections命名空間下提供了一系列實(shí)現(xiàn)集合功能的類(lèi),并且根據(jù)適用環(huán)境的不同為開(kāi)發(fā)者提供靈活多樣的選擇性:如通過(guò)索引訪問(wèn)使用廣泛的arraylist 和 stringcollection;通常在檢索后被釋放的先進(jìn)先出的queue和后進(jìn)先出stack;通過(guò)元素鍵對(duì)其元素進(jìn)行訪問(wèn)hashtable、sortedlist、listdictionary 和 stringdictionary;通過(guò)索引或通過(guò)元素鍵對(duì)其元素進(jìn)行訪問(wèn)的nameobjectcollectionbase 和 namevaluecollection;以及具有集合類(lèi)的特性而被實(shí)現(xiàn)在system.array下的array類(lèi)等。本文將通過(guò)實(shí)現(xiàn)具有代表性的 “集合類(lèi)”的兩種典型途徑,分析對(duì)比不同實(shí)現(xiàn)方式的差異性與適用環(huán)境,讓大家了解和掌握相關(guān)的一些技術(shù),希望為大家的學(xué)習(xí)和開(kāi)發(fā)工作起到拋磚引玉的作用(注:作者的調(diào)試運(yùn)行環(huán)境為.net framework sdk 1.1)。
1.采用從collectionbase抽象基類(lèi)繼承的方式實(shí)現(xiàn)customers集合類(lèi):
首先需要?jiǎng)?chuàng)建為集合提供元素的簡(jiǎn)單類(lèi)customer:
 
 /// <summary>
 /// 描述一個(gè)客戶基本信息的類(lèi)
 /// </summary>
 public class customer
 {
 /// <summary>
 /// 客戶姓名
 /// </summary>
 public string name;
 
 /// <summary>
 /// 描述所有客戶聯(lián)絡(luò)信息的集合類(lèi)
 /// </summary>
 //public contacts contacts=new contacts(); 
 
 /// <summary>
 /// 不帶參數(shù)的customer類(lèi)構(gòu)造函數(shù)
 /// </summary>
 public customer()
 {
 system.console.writeline("initialize instance without parameter");
 }
 
 /// <summary>
 /// 帶參數(shù)的customer類(lèi)構(gòu)造函數(shù)
 /// </summary>
 public customer(string name)
 {
 name=name;
system.console.writeline("initialize instance with parameter");
 }
 }
 
以上就是customer類(lèi)的簡(jiǎn)單框架,實(shí)用的customer類(lèi)可能擁有更多的字段、屬性、方法和事件等。值得注意的是在customer類(lèi)中還以公共字段形式實(shí)現(xiàn)了對(duì)contacts集合類(lèi)的內(nèi)聯(lián),最終可形成customer.contacts[i]的接口形式,但這并不是最理想的集合類(lèi)關(guān)聯(lián)方式,暫時(shí)將它注釋,稍后將詳加分析,這個(gè)類(lèi)的代碼重在說(shuō)明一個(gè)簡(jiǎn)單類(lèi)(相對(duì)于集合類(lèi)的概念范疇)的框架;另外,該類(lèi)還對(duì)類(lèi)構(gòu)造函數(shù)進(jìn)行了重載,為聲明該類(lèi)的實(shí)例時(shí)帶name參數(shù)或不帶參數(shù)提供選擇性。
接下來(lái)看我們的第一種集合類(lèi)實(shí)現(xiàn),基于從collectionbase類(lèi)派生而實(shí)現(xiàn)的customers類(lèi):
 /// <summary>
 /// customers 是customer的集合類(lèi)實(shí)現(xiàn),繼承自collectionbase
 /// </summary>
 public class customers: system.collections.collectionbase 
 {
 public customers()
 {
 
 }
/// <summary>
/// 自己實(shí)現(xiàn)的add方法
/// </summary>
/// <param name="customer"></param>
 public void add(customer customer)
 {
 list.add(customer);
 }
/// <summary>
/// 自己實(shí)現(xiàn)的remove方法
/// </summary>
/// <param name="index"></param>
 public void remove(int index)
 {
 if (index > count - 1 || index < 0)
 {
 system.console.writeline("index not valid!");
 }
 else
 {
 list.removeat(index); 
 }
 }
 }
 
以customers集合類(lèi)為例,結(jié)合集合輔助技術(shù),希望大家能了解掌握以下知識(shí):
從collectionbase繼承實(shí)現(xiàn)集合類(lèi)
customers類(lèi)采用從collectionbase繼承的方式,不再需要在類(lèi)內(nèi)聲明一個(gè)作為customer集合容器的list對(duì)象,因?yàn)閏ollectionbase類(lèi)已經(jīng)內(nèi)置了一個(gè)list對(duì)象,并已經(jīng)實(shí)現(xiàn)了count、clear、removeat等等ilist的重要接口(具體請(qǐng)參照msdn中的collectionbase 成員),只需要用戶顯示實(shí)現(xiàn)add、remove、indexof、insert等等接口,代碼中僅簡(jiǎn)單實(shí)現(xiàn)了add方法和remove方法的整參數(shù)版本作為示例。這種集合類(lèi)的實(shí)現(xiàn)具有簡(jiǎn)單高效的特點(diǎn),collectionbase已經(jīng)實(shí)現(xiàn)了較為完善的功能,實(shí)施者只要在其基礎(chǔ)上擴(kuò)展自己所需的功能即可。
 
索引器的簡(jiǎn)單實(shí)現(xiàn)
 我們慣于操作數(shù)組的形式通常為array[i],集合類(lèi)可以看作是“對(duì)象的數(shù)組”,在c#中,幫助集合類(lèi)實(shí)現(xiàn)數(shù)組式索引功能的就是索引器:
 public customer this[int index]
 {
 get
 {
 return (customer) list[index];
 }
 }
將以上代碼加入到customers類(lèi)后,就實(shí)現(xiàn)了以整形index為參數(shù),以list[index]強(qiáng)制類(lèi)型轉(zhuǎn)換后的customer類(lèi)型返回值的customers類(lèi)只讀索引器,使用者以customers[i].name的方式,就可以訪問(wèn)customers集合中第i個(gè)customer對(duì)象的姓名字段,是不是很神奇呢?文中的索引器代碼并未考慮下標(biāo)越界的問(wèn)題,越界的處理方式應(yīng)參照與之類(lèi)似的remove方法。作者在此只實(shí)現(xiàn)了索引器的get訪問(wèn),沒(méi)有實(shí)現(xiàn)set訪問(wèn)的原因?qū)⒃谙挛闹杏懻摗?br> 
item的兩種實(shí)現(xiàn)方式
用過(guò)vb的朋友們一定都很熟悉customers.itme(i).name的形式,它實(shí)現(xiàn)了與索引器相同的作用,即通過(guò)一個(gè)索引值來(lái)訪問(wèn)集合體中的特定對(duì)象,但item在c#當(dāng)中應(yīng)該以怎樣的形式實(shí)現(xiàn)呢?首先想到的實(shí)現(xiàn)途徑應(yīng)該是屬性,但你很快就會(huì)發(fā)現(xiàn)c#的屬性是不支持參數(shù)的,所以無(wú)法把索引值作為參數(shù)傳入,折中的辦法就是以方法來(lái)實(shí)現(xiàn):
 public customer item (int index)
 {
 return (customer) list[index];
 }
這個(gè)item方法已經(jīng)可以工作了,但為什么說(shuō)是折中的辦法呢,因?yàn)閷?duì)item的訪問(wèn)將是采用customers.item(i).name的語(yǔ)法形式,與c#‘[]’作數(shù)組下標(biāo)的風(fēng)格不統(tǒng)一,顯的有些突兀,但如果希望在語(yǔ)法上做到統(tǒng)一,哪怕是性能受一些影響也無(wú)所謂的話有沒(méi)有解決之道呢?請(qǐng)看以下代碼:
 public customers item 
 {
 get
 {
 return this;
 }
 }
這是以屬性形式實(shí)現(xiàn)的item接口,但是由于c#的屬性不支持參數(shù),所以我們返回customers對(duì)象本身,也就是在調(diào)用customers對(duì)象item屬性時(shí)會(huì)引發(fā)對(duì)customers索引器的調(diào)用,性能有所下降,但是的確實(shí)現(xiàn)了customers.item[i].name的語(yǔ)法風(fēng)格統(tǒng)一。對(duì)比這兩種item的實(shí)現(xiàn),不難得出結(jié)論:以不帶參數(shù)的屬性形式實(shí)現(xiàn)的item依賴于類(lèi)的索引器,如果該類(lèi)沒(méi)有實(shí)現(xiàn)索引器,該屬性將無(wú)法使用;并且由于對(duì)item的訪問(wèn)重定向到索引器性能也會(huì)下降;唯一的理由是:統(tǒng)一的c#索引下標(biāo)訪問(wèn)風(fēng)格;采用方法實(shí)現(xiàn)的裨益正好與之相反,除了語(yǔ)法風(fēng)格較為別扭外,不存在依賴索引器、性能下降的問(wèn)題。魚(yú)與熊掌難以兼得,如何取舍應(yīng)依據(jù)開(kāi)發(fā)的實(shí)際需求決定。
中間語(yǔ)言的編譯缺省與attribute的應(yīng)用
如果你既實(shí)現(xiàn)了標(biāo)準(zhǔn)的索引器,又想提供名為“item”的接口,編譯時(shí)就會(huì)出現(xiàn)錯(cuò)誤“類(lèi)‘windowsapplication1.customers’已經(jīng)包含了“item”的定義”,但除了建立索引器外,你什么也沒(méi)有做,問(wèn)題到底出在哪里?我們不得不從.net中間語(yǔ)言il來(lái)尋找答案了,在.net命令行環(huán)境或visual studio .net 命令提示環(huán)境下,輸入ildasm,運(yùn)行.net framework msil 反匯編工具,通過(guò)主菜單中的‘打開(kāi)’加載只有索引器沒(méi)有item接口實(shí)現(xiàn)的可以編譯通過(guò)的.net pe執(zhí)行文件,通過(guò)直觀的樹(shù)狀結(jié)構(gòu)圖找到customers類(lèi),你將意外地發(fā)現(xiàn)c#的索引器被解釋成了一個(gè)名為item的屬性,以下是il反編譯后的被定義為item屬性的索引器代碼:
.property instance class windowsapplication1.customer
 item(int32)
{
 .get instance class windowsapplication1.customer windowsapplication1.customers::get_item(int32)
} // end of property customers::item
問(wèn)題總算水落石出,就是c#編譯器‘自作聰明’地把索引器解釋成了一個(gè)名為item的屬性,與我們期望實(shí)現(xiàn)的item接口正好重名,所以出現(xiàn)上述的編譯錯(cuò)誤也就在所難免。那么,我們有沒(méi)有方法告知編譯器,不要將索引器命名為缺省item呢?答案是肯定的。
解決方法就是在索引器實(shí)現(xiàn)之前聲明特性:
[system.runtime.compilerservices.indexername("item")] 
定義這個(gè)indexername特性將告知csharp編譯器將索引器編譯成item而不是默認(rèn)的item ,修改之后的索引器il反匯編代碼為:
.property instance class windowsapplication1.customer
 item(int32)
{
 .get instance class windowsapplication1.customer windowsapplication1.customers::get_item(int32)
} // end of property customers::item
當(dāng)然你可以將索引器的生成屬性名定義成其它名稱而不僅限于item,只要不是il語(yǔ)言的保留關(guān)鍵字就可以。經(jīng)過(guò)了給索引器命名,你就可以自由地加入名為“item”的接口實(shí)現(xiàn)了。
 
 以下為customer類(lèi)和customers類(lèi)的調(diào)試代碼,在作者的customers類(lèi)中,為說(shuō)明問(wèn)題,同時(shí)建立了以item為特性名的索引器、一個(gè)items方法和一個(gè)item屬性來(lái)實(shí)現(xiàn)對(duì)集合元素的三種不同訪問(wèn)方式,實(shí)際的項(xiàng)目開(kāi)發(fā)中,一個(gè)類(lèi)的索引功能不需要重復(fù)實(shí)現(xiàn)多次,可能只實(shí)現(xiàn)索引器或一個(gè)索引器加上一種形式的item就足夠了:
 public class calltest
 {
 public static void main() 
 {
 customers custs=new customers();
 system.console.writeline(custs.count.tostring());//count屬性測(cè)試
 
 customer acust=new customer();//將調(diào)用不帶參數(shù)的構(gòu)造函數(shù)
 acust.name ="peter";
 custs.add(acust);//add方法測(cè)試
 
 system.console.writeline(custs.count.tostring());
 system.console.writeline(custs.item[0].name);//調(diào)用item屬性得到
 custs.items(0).name+="hu";//調(diào)用items方法得到
 system.console.writeline(custs[0].name);//調(diào)用索引器得到
 
 custs.add(new customer("linnet"));//將調(diào)用帶name參數(shù)的構(gòu)造函數(shù)
 system.console.writeline(custs.count.tostring());
 system.console.writeline(custs.items(1).name);//調(diào)用items方法得到
 custs.item[1].name+="li";//調(diào)用items方法得到
 system.console.writeline(custs[1].name);//調(diào)用索引器得到
 
 custs.remove(0);//remove方法測(cè)試
 system.console.writeline(custs.count.tostring());
 system.console.writeline(custs[0].name);//remove有效性驗(yàn)證
 custs[0].name="test passed" ;//調(diào)用索引器得到
 system.console.writeline(custs.item[0].name);
 custs.clear(); 
 system.console.writeline(custs.count.tostring());//clear有效性驗(yàn)證
 
 }
 }
輸出結(jié)果為:
0
initialize instance without parameter
1
peter
peterhu
initialize instance with parameter
2
linnet
linnetli
1
linnetli
test passed
0
 
2.采用內(nèi)建arraylist對(duì)象的方式實(shí)現(xiàn)集合類(lèi):
或許有經(jīng)驗(yàn)的程序員們?cè)缫呀?jīng)想到,可以在一個(gè)類(lèi)中內(nèi)建一個(gè)數(shù)組對(duì)象,并在該類(lèi)中通過(guò)封裝對(duì)該對(duì)象的訪問(wèn),一樣能夠?qū)崿F(xiàn)集合類(lèi)。以下是采用這種思路的contact元素類(lèi)和contacts集合類(lèi)的實(shí)現(xiàn)框架:
 
 public class contact
 {
 protected string summary;
 
 /// <summary>
 /// 客戶聯(lián)系說(shuō)明
 /// </summary>
 public string summary
 {
 get
 {
 system.console.writeline("getter access");
 return summary;//do something, as get data from data source
 }
 set
 {
 system.console.writeline("setter access");
 summary=value;// do something , as check validity or storage
 }
 }
 
 public contact()
 {
 
 }
 }
 
 public class contacts
 {
 protected arraylist list; 
 
 public void add(contact contact)
 {
 list.add(contact);
 }
 
 public void remove(int index)
 {
 if (index > list.count - 1 || index < 0)
 {
 system.console.writeline("index not valid!");
 }
 else
 {
 list.removeat(index); 
 }
 }
 
 public int count
 {
 get
 {
 return list.count; 
 }
 }
 
 public contact this[int index]
 {
 get
 {
 system.console.writeline("indexer getter access");
 return (contact) list[index];
 }
 set
 {
 list[index]=value;
 system.console.writeline("indexer setter access ");
 }
 
 }
 
 public contacts()
 {
 list=new arraylist(); 
 }
 }
通過(guò)這兩個(gè)類(lèi)的實(shí)現(xiàn),我們可以總結(jié)以下要點(diǎn):
采用arraylist的原因
在contacts實(shí)現(xiàn)內(nèi)置集合對(duì)象時(shí),使用了arraylist類(lèi),而沒(méi)有使用大家較為熟悉的array類(lèi),主要的原因有:在現(xiàn)有的.net v1.1環(huán)境中,array雖然已經(jīng)暴露了ilist.add、ilist.insert、ilist.remove、ilist.removeat等典型的集合類(lèi)接口,而實(shí)際上實(shí)現(xiàn)這些接口總是會(huì)引發(fā) notsupportedexception異常,microsoft是否在未來(lái)版本中實(shí)現(xiàn)不得而知,但目前版本的.net顯然還不支持動(dòng)態(tài)數(shù)組,在ms推薦的更改array大小的辦法是,將舊數(shù)組通過(guò)拷貝復(fù)制到期望尺寸的新數(shù)組后,刪除舊數(shù)組,這顯示是費(fèi)時(shí)費(fèi)力地在繞彎路,無(wú)法滿足集合類(lèi)隨時(shí)添加刪除元素的需求;arraylist已經(jīng)實(shí)現(xiàn)了add、clear、count、indexof、insert、remove、removeat等集合類(lèi)的關(guān)鍵接口,并且有支持只讀集合的能力,在上邊的contacts類(lèi)中,只通過(guò)極少的封裝代碼,就輕松地實(shí)現(xiàn)了集合類(lèi)。另一個(gè)問(wèn)題是我們?yōu)槭裁床徊捎门ccustomers類(lèi)似的從system.collections.arraylist繼承的方式實(shí)現(xiàn)集合類(lèi)呢?主要是由于將arraylist對(duì)象直接暴露于類(lèi)的使用者,將導(dǎo)致非法的賦值,如用戶調(diào)用arraylist.add方法,無(wú)論輸入的參數(shù)類(lèi)型是否為contact,方法都將被成功執(zhí)行,類(lèi)無(wú)法控制和檢查輸入對(duì)象的類(lèi)型與期望的一致,有悖該類(lèi)只接納contact類(lèi)型對(duì)象的初衷,也留下了極大的安全隱患;并且在contact對(duì)象獲取時(shí),如不經(jīng)過(guò)強(qiáng)制類(lèi)型轉(zhuǎn)換,contacts元素也無(wú)法直接以contact類(lèi)型形式來(lái)使用。
集合類(lèi)中的set
 在集合類(lèi)的實(shí)現(xiàn)過(guò)程中,無(wú)論是使用索引器還是與索引器相同功能的“item”屬性,無(wú)可避免地會(huì)考慮是只實(shí)現(xiàn)getter形成只讀索引器,還是同時(shí)實(shí)現(xiàn)getter和setter形成完整的索引器訪問(wèn)。在上文的示例類(lèi)customers中就沒(méi)有實(shí)現(xiàn)索引器的setter,形成了只讀索引器,但在customer類(lèi)和customers類(lèi)的調(diào)試代碼,作者使用了容易令人迷惑的“custs[0].name="test passed"”的訪問(wèn)形式,事實(shí)上,以上這句并不會(huì)進(jìn)入到customers索引器的setter而是會(huì)先執(zhí)行customers索引器的getter得到一個(gè)customer對(duì)象,然后設(shè)置這個(gè)customer的name字段(如果name元素為屬性的話,將訪問(wèn)customer類(lèi)name屬性的setter)。那么在什么情況下索引器的setter才會(huì)被用到呢?其實(shí)只有需要在運(yùn)行時(shí)動(dòng)態(tài)地覆蓋整個(gè)元素類(lèi)時(shí),集合類(lèi)的setter才變得有意義,如“custs [i]=new customer ()”把一個(gè)全新的customer對(duì)象賦值給custs集合類(lèi)的已經(jīng)存在的一個(gè)元素,這樣的訪問(wèn)形式將導(dǎo)致customers的setter被訪問(wèn),即元素對(duì)象本身進(jìn)行了重新分配,而不僅僅是修改現(xiàn)有對(duì)象的一些屬性。也就是說(shuō),由于customers類(lèi)沒(méi)有實(shí)現(xiàn)索引器的setter 所以customers類(lèi)對(duì)外不提供“覆蓋”客戶集合中既有客戶的方法。與此形成鮮明對(duì)照的是contacts類(lèi)的索引器既提供對(duì)集合元素的getter,又提供對(duì)集合元素的setter,也就是說(shuō)contacts類(lèi)允許使用者動(dòng)態(tài)地更新contact元素。通過(guò)對(duì)contacts和contact兩個(gè)類(lèi)運(yùn)行以下測(cè)試可以很明確說(shuō)明這個(gè)問(wèn)題:
 public class calltest
 {
 public static void main() 
 {
 contacts cons=new contacts();
 cons.add(new contact());
 cons[0]=new contact();//trigger indexer setter
 cons[0].summary="mail contact about ticket";
 system.console.writeline(cons[0].summary); 
 }
 }
理所當(dāng)然的輸出結(jié)果為:
indexer setter access
indexer getter access
setter access
indexer getter access
getter access
mail contact about ticket
明確認(rèn)識(shí)到了索引器setter的作用后,在類(lèi)的實(shí)現(xiàn)中就應(yīng)當(dāng)綜合實(shí)際業(yè)務(wù)特點(diǎn)、存取權(quán)限控制和安全性決定是否為索引器建立setter機(jī)制。
屬性-強(qiáng)大靈活的字段 合二為一的方法
 在最初實(shí)現(xiàn)customer類(lèi)時(shí),我們使用了一個(gè)公共字段name,用作存取客戶的姓名信息,雖然可以正常的工作,但我們卻缺乏對(duì)name字段的控制能力,無(wú)論類(lèi)的使用者是否使用了合法有效的字段賦值,字段的值都將被修改;并且沒(méi)有很好的機(jī)制,在值改變時(shí)進(jìn)行實(shí)時(shí)的同步處理(如數(shù)據(jù)存儲(chǔ),通知相關(guān)元素等);另外,字段的初始化也只能放在類(lèi)的構(gòu)造函數(shù)中完成,即使在整個(gè)對(duì)象生命周期內(nèi)name字段都從未被訪問(wèn)過(guò)。對(duì)比我們?cè)赾ontact類(lèi)中實(shí)現(xiàn)的summary屬性,不難發(fā)現(xiàn),屬性所具有的優(yōu)點(diǎn):屬性可以在get時(shí)再進(jìn)行初始化,如果屬性涉及網(wǎng)絡(luò)、數(shù)據(jù)庫(kù)、內(nèi)存和線程等資源占用的方式,推遲初始化的時(shí)間,將起到一定的優(yōu)化作用;經(jīng)過(guò)屬性的封裝,真正的客戶聯(lián)系說(shuō)明summary被很好地保護(hù)了起來(lái),在set時(shí),可以經(jīng)過(guò)有效性驗(yàn)證再進(jìn)行賦值操作;并且在getter和setter前后,可以進(jìn)行數(shù)據(jù)存取等相關(guān)操作,這一點(diǎn)用字段是不可能實(shí)現(xiàn)的。所以我們可以得出結(jié)論,在字段不能滿足需求的環(huán)境中,屬性是更加強(qiáng)大靈活的替代方式。
另外,屬性整合了“get”和“set”兩個(gè)“方法”,而采用統(tǒng)一自然的接口名稱,較之java語(yǔ)言的object.getanything和object.setanything語(yǔ)法風(fēng)格更加親和(事實(shí)上,c#中的屬性只不過(guò)是對(duì)方法的再次包裝,具有g(shù)etter和setter的anything屬性在.net il中,依然會(huì)被分解成一個(gè)由anything屬性調(diào)用的get_anything和set_anything兩個(gè)方法)。
集合類(lèi)內(nèi)聯(lián)的方式
在文章最初的customer類(lèi)中使用了公共字段public contacts contacts=new contacts()實(shí)現(xiàn)了customer. contacts[]形式的集合類(lèi)內(nèi)聯(lián)接口,這是一種最為簡(jiǎn)單但缺乏安全性保護(hù)的集合類(lèi)集成方式,正如以上所述屬性的一些優(yōu)點(diǎn),采用屬性形式暴露一個(gè)公共的集合類(lèi)接口,在實(shí)際存取訪問(wèn)時(shí),再對(duì)受封狀保護(hù)的集合類(lèi)進(jìn)行操作才是更為妥當(dāng)完善的解決方案,如可以把customer類(lèi)內(nèi)聯(lián)的集合contacts的接口聲明改為:
 protected contacts cons; //用于類(lèi)內(nèi)封裝的真正contacts對(duì)象
 public contacts contacts//暴露在類(lèi)外部的contacts屬性
 {
 get
 {
 if (cons == null) cons=new contacts();
 return cons;
 }
 set
 {
 cons=value;
 }
 }
最終,customers[i].contacts[x].summary的形式就被成功地實(shí)現(xiàn)了。
實(shí)例化的最佳時(shí)機(jī)
 .net的類(lèi)型系統(tǒng)是完全對(duì)象化的,所有的類(lèi)型都是從system.object派生而來(lái),根據(jù)類(lèi)型的各自特點(diǎn),可以分為值類(lèi)型和引用類(lèi)型兩大陣營(yíng)。值類(lèi)型包括結(jié)構(gòu)(簡(jiǎn)單的數(shù)值型和布爾型也包括在內(nèi))和枚舉,引用類(lèi)型則包括了類(lèi)、數(shù)組、委托、接口、指針等,對(duì)象化的一個(gè)特點(diǎn)是直到對(duì)象實(shí)例化時(shí)才為對(duì)象分配系統(tǒng)資源,也就是說(shuō)靈活適時(shí)地實(shí)例化對(duì)象,對(duì)系統(tǒng)資源的優(yōu)化分配將產(chǎn)生積極意義。在一些文章中所建議的“l(fā)azy initialization”倡導(dǎo)在必要時(shí)才進(jìn)行對(duì)象的實(shí)例化,本著這樣的原則,從類(lèi)的外部來(lái)看,類(lèi)可以在即將被使用時(shí)再進(jìn)行初始化;在類(lèi)的內(nèi)部,如屬性之類(lèi)的元素,也可以不在構(gòu)造函數(shù)中初始化,而直到屬性的getter被真正訪問(wèn)時(shí)才進(jìn)行,如果屬性一直沒(méi)有被讀取過(guò),就不必要無(wú)意義地占用網(wǎng)絡(luò)、數(shù)據(jù)庫(kù)、內(nèi)存和線程等資源了。但是也并不是初始化越晚越好,因?yàn)槌跏蓟切枰獣r(shí)間的,在使用前才進(jìn)行初始化可能導(dǎo)致類(lèi)的響應(yīng)速度過(guò)慢,無(wú)法適應(yīng)使用者的實(shí)時(shí)需求。所以在資源占用和初始化耗時(shí)之間尋求一個(gè)平衡點(diǎn),才是實(shí)例化的最佳時(shí)機(jī)。
 
總結(jié)
 本文圍繞實(shí)現(xiàn)集合類(lèi)的兩種途徑-從collectionbase繼承實(shí)現(xiàn)和內(nèi)建arraylist對(duì)象實(shí)現(xiàn),為大家展示了部分集合、索引器、屬性、特性的應(yīng)用以及.net環(huán)境中的類(lèi)構(gòu)造函數(shù)、對(duì)象優(yōu)化、類(lèi)關(guān)聯(lián)等其它相關(guān)知識(shí)。通過(guò)本文淺顯的示例和闡述,希望可以啟發(fā)讀者的靈感,推出更加精辟合理的基礎(chǔ)理論和應(yīng)用模型。