在真正的對象化開發項目中,我們通常會將常用的業務實體抽象為特定的類,如Employee、Customer、Contact等,而多數的類之間會存在著相應的關聯或依存關系,如Employee和Customer通過Contact而產生關聯、Contact是依賴於Employee和Customer而存在的。在實際的對象應用模塊中,可能會有這樣的需求:獲得一組客戶對象(即Customers集合類的實例,如customers),指向其中一個Customer對象(如customers[i]),通過訪問這個Customer對象的屬性Name(customers[i].Name)和Contacts(如customers[i].Contacts)來查詢客戶的姓名和與該客戶的聯絡記錄,甚至遍歷Contacts對象,查找該客戶的某次聯絡摘要(即customers.[i].contacts[x].Summary)。為滿足以上集合類的需求,對照.NET Framework 的平台實現,不難發現.NET在Collections命名空間下提供了一系列實現集合功能的類,並且根據適用環境的不同為開發者提供靈活多樣的選擇性:如通過索引訪問使用廣泛的ArrayList 和 StringCollection;通常在檢索後被釋放的先進先出的Queue和後進先出Stack;通過元素鍵對其元素進行訪問Hashtable、SortedList、ListDictionary 和 StringDictionary;通過索引或通過元素鍵對其元素進行訪問的NameObjectCollectionBase 和 NameValueCollection;以及具有集合類的特性而被實現在System.Array下的Array類等。本文將通過實現具有代表性的 “集合類”的兩種典型途徑,分析對比不同實現方式的差異性與適用環境,讓大家了解和掌握相關的一些技術,希望為大家的學習和開發工作起到拋磚引玉的作用(注:作者的調試運行環境為.NET Framework SDK 1.1)。
1.采用從CollectionBase抽象基類繼承的方式實現Customers集合類:
首先需要創建為集合提供元素的簡單類Customer:
/// <summary>
/// 描述一個客戶基本信息的類
/// </summary>
public class Customer
{
/// <summary>
/// 客戶姓名
/// </summary>
public string Name;
/// <summary>
/// 描述所有客戶聯絡信息的集合類
/// </summary>
//public Contacts Contacts=new Contacts();
/// <summary>
/// 不帶參數的Customer類構造函數
/// </summary>
public Customer()
{
System.Console.WriteLine("Initialize instance without parameter");
}
/// <summary>
/// 帶參數的Customer類構造函數
/// </summary>
public Customer(string name)
{
Name=name;
System.Console.WriteLine("Initialize instance with parameter");
}
}
以上就是Customer類的簡單框架,實用的Customer類可能擁有更多的字段、屬性、方法和事件等。值得注意的是在Customer類中還以公共字段形式實現了對Contacts集合類的內聯,最終可形成Customer.Contacts[i]的接口形式,但這並不是最理想的集合類關聯方式,暫時將它注釋,稍後將詳加分析,這個類的代碼重在說明一個簡單類(相對於集合類的概念范疇)的框架;另外,該類還對類構造函數進行了重載,為聲明該類的實例時帶name參數或不帶參數提供選擇性。
接下來看我們的第一種集合類實現,基於從CollectionBase類派生而實現的Customers類:
/// <summary>
/// Customers 是Customer的集合類實現,繼承自CollectionBase
/// </summary>
public class Customers: System.Collections.CollectionBase
{
public Customers()
{
}
/// <summary>
/// 自己實現的Add方法
/// </summary>
/// <param name="customer"></param>
public void Add(Customer customer)
{
List.Add(customer);
}
/// <summary>
/// 自己實現的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集合類為例,結合集合輔助技術,希望大家能了解掌握以下知識:
從CollectionBase繼承實現集合類
Customers類采用從CollectionBase繼承的方式,不再需要在類內聲明一個作為Customer集合容器的List對象,因為CollectionBase類已經內置了一個List對象,並已經實現了Count、Clear、RemoveAt等等IList的重要接口(具體請參照MSDN中的CollectionBase 成員),只需要用戶顯示實現Add、Remove、IndexOf、Insert等等接口,代碼中僅簡單實現了Add方法和Remove方法的整參數版本作為示例。這種集合類的實現具有簡單高效的特點,CollectionBase已經實現了較為完善的功能,實施者只要在其基礎上擴展自己所需的功能即可。
索引器的簡單實現
我們慣於操作數組的形式通常為array[i],集合類可以看作是“對象的數組”,在C#中,幫助集合類實現數組式索引功能的就是索引器:
public Customer this[int index]
{
get
{
return (Customer) List[index];
}
}
將以上代碼加入到Customers類後,就實現了以整形index為參數,以List[index]強制類型轉換後的Customer類型返回值的Customers類只讀索引器,使用者以Customers[i].Name的方式,就可以訪問Customers集合中第i個Customer對象的姓名字段,是不是很神奇呢?文中的索引器代碼並未考慮下標越界的問題,越界的處理方式應參照與之類似的Remove方法。作者在此只實現了索引器的get訪問,沒有實現set訪問的原因將在下文中討論。
Item的兩種實現方式
用過VB的朋友們一定都很熟悉Customers.Itme(i).Name的形式,它實現了與索引器相同的作用,即通過一個索引值來訪問集合體中的特定對象,但Item在C#當中應該以怎樣的形式實現呢?首先想到的實現途徑應該是屬性,但你很快就會發現C#的屬性是不支持參數的,所以無法把索引值作為參數傳入,折中的辦法就是以方法來實現: