天天看點

徹底剖析C#2.0泛型類的建立和使用

 "一次編碼,多次使用",這就是引入泛型的根源。在以前的C++中稱為模闆,C#泛型通過算法和資料結構支援獨立編碼。例如,泛型清單意味着,你不必再重寫一個強類型集合。在本文中,作者将向你展示定義和使用泛型是多麼容易的事情-請注意,長期以來泛型一直被認為是最進階和最困難的術語。

  一、 簡介

  泛型現在在任何一種語言中都被認為是一個進階的強有力的術語。當我在C++中第一次接觸模闆時,我對之有些疑惑。之後,我讀了Bjarne Stroustrop的《The Design and Evolution of C++》,才發現模闆的使用就象C中的宏和用之來取代的簡單串替換模闆一樣容易。其實,模闆和泛型是相同的東西-盡管它們的實作稍微不同。

  C#泛型支援在使用點處才定義算法及其資料類型。在C#的一些早期版本中,我們可以證明沒有泛型也可以工作,因為每種類型都是派生于一個公共基類型-object。這意味着程式員可以基于object類型定義一個棧類并且把一切東西放到該棧上(因為一切都派生于object)。然而,一個object棧意味着,Customer對象,Integer對象以及假想的對象都能被放置到同一個棧的執行個體上。結果是,開發者要子類化資料類型來把資料類型綁定到他們要與之互動的東西上去。例如,在編寫定制的商業對象時,我們就建議定義派生于System.Collections.CollectionBase的強類型集合。原因很簡單:基于object定義一切被認為是弱類型定義。

  業界的高手們在數十年前就确信強類型優于弱類型,是以.NET最終支援強類型,這看上去是很自然的事情。強類型算法當然建議類型化參數-這正是我們在泛型中所用的東西。

  十幾年來,我們一直在使用字母T作為類型化參數的名字。這樣,在任何泛型類使用者所提供的資料類型的地方,你都能夠找到T。使用泛型的關鍵僅僅是提供這個T。定義泛型的關鍵在于實作一個方法或類,并且用特定資料類型來替換掉T。

  C#中的泛型支援另外一些提煉。例如,一個方法或類可以有多個參數化的類型并且C#泛型還支援WHERE限制-它用來具體要求類型化參數的類型。例如,如果一個泛型類型必須實作接口IDisposable,那麼C#泛型是支援實作這一限制的。在文章的最後我們還要看一下限制問題。

  閑話少說,讓我們言歸正傳。

  二、 使用泛型集合

  有些人問我"面向對象程式設計(OOP)的承諾在哪裡?",我的回答是應該從兩個方面來看OOP:你所使用的OOP和你建立的OOP。如果我們簡單地看一下如果沒有如例如Microsoft的.NET,Borland的VCL,以及所有的第三方元件這樣的OO架構,那麼很多進階的應用程式幾乎就無法建立。是以,我們可以說OOP已經實作了它的承諾。不錯,生産好的OOP代碼是困難的并且可能是極具挫敗性的;但是記住,你不必須一定要通過OOP來實作你的目标。是以,下面首先讓我們看一下泛型的使用。

  當你用Visual Studio或C# Express等快速開發工具建立工程時,你會看到對于System.Collections.Generic命名空間的參考引用。在這個命名空間中,存在若幹泛型資料結構-它們都支援類型化的集合,散列,隊列,棧,字典以及連結清單等。為了使用這些強有力的資料結構,你所要做的僅是提供資料類型。

  清單1顯示出我們定義一個強類型集合的Customer對象是很容易的。

  清單1 這個控制台應用程式包含一個Customer類和一個基于List<T>的強類型集合Customers。

  using System;

  using System.Collections.Generic;

  using System.Text;

  namespace Generics{

  class Program{

  static void Main(string[] args){

  List<Customer> customers = new List<Customer>();

  customers.Add(new Customer("Motown-Jobs"));

  customers.Add(new Customer("Fatman's"));

  foreach (Customer c in customers)

  Console.WriteLine(c.CustomerName);

  Console.ReadLine();

  }

  public class Customer{

  private string customerName = "";

  public string CustomerName{

  get { return customerName; }

  set { customerName = value; }

  public Customer(string customerName){

  this.customerName = customerName;

  注意,我們有一個強類型集合-List<Customer>-對這個集合類本身來說不需要寫一句代碼。如果我們想要擴充清單customer,我們可以通過從List<Customer>繼承而派生一個新類。

  三、 實作一個泛型類

  一種合理的實作某種新功能的方法是在原有的事物上進一步建構。我們已經了解強類型集合,并知道一種不錯的用來建構泛型類的技術是使用一個特定類并删除資料類型。也就是說,讓我們定義一個強類型集合CustomerList,并且來看一下它要把什麼東西轉化成一個泛型類。

  清單2定義了一個類CustomerList。後面的部分把CustomerList轉化成List<T>。

  清單2定義類CustomerList:

  using System.Collections;

  public class CustomerList : CollectionBase{

  public CustomerList() { }

  public Customer this[int index]{

  get { return (Customer)List[index]; }

  set { List[index] = value; }

  public int Add(Customer value)

  {return List.Add(value);}

  四、 定義類頭

  如果我們定義一個泛型類,我們需要把類頭轉化成一個泛型類。所有我們需要做的是命名參數并且把類名改成某種泛型。List<T>隻有一個參數T,并且因為我們在以一種向後相容的方式工作,是以我們知道類名是List。清單3顯示出清單2中類的新類頭。

  清單3 一個泛型類頭顯示出參數化的參數T。

  public class List<T> : CollectionBase {}

  五、 實作泛型字段

  如果我們需要把任何字段轉換成泛型字段,我們将隻需簡單地把它們的類型改變成T(或該字段所描述的任何參數)。泛型List不需要任何字段,但是假定存在一個私有的整型字段叫foo-我們将把它泛型化。我們将如下重新定義它:

  private T foo;

  當參數T被填充到類中時,List T也将因foo被填充。

  六、 定義泛型方法

  接下來,我們為所需要的參數化類型定義其它一些特性。這包括屬性,方法,和事件。在我們的執行個體中,在Customer出現的每一處,我們都用參數T替換它。完成後的泛型清單類顯示于清單4中。

  清單4 一個基于System.Collections.CollectionBase的輕量級的參數化泛型清單類。

  public class List<T> : CollectionBase {

  public List(){ }

  public T this[int index] {

  get { return (T)List[index]; }

  public int Add(T value) {

  return List.Add(value);

  為了測試該定制清單,注釋掉使用System.Collections.Generic命名空間一句并且把清單4中的List<T>使用在清單1的代碼中;它将以同樣的方式工作。

  全面地修改.NET的List<T>是不必要的而且它也包含遠比我們的示例多得多的特性;但是清單4顯示出這種機制對于定義定制泛型類是多麼容易。

  七、 增加類型限制

  最後要讨論的是限制問題。限制被應用于類或其它特性上并且使用下面的文法:

  Where T : constraint_type

  例如,任何我們想要通過using語句所使用的,如一個SqlDataReader,必須實作Idisposable接口。這是因為如下方式使用的using語句:

  using(Form f = new Form()){...}

  就象一個try..finally塊一樣工作-總是清除新建立的資源。其工作原理很簡單,隻需要CLR針對在該using語句中建立的對象發出一個到IDisposable.Dispose的調用即可。例如,在上面這一句中,一個新的表單被建立,并且在using語句退出之前即調用Form.Dispose。

  要對一個泛型類施加以確定該類實作了接口IDisposable,我們将添加先行詞where T:Idisposable。在清單4中的泛型清單上施加限制後,我們将重新修改清單4如下面的清單5所示。

  清單5 增加一個限制到泛型類以確定我們的List<T>中的所有的值T實作接口Idisposable。

  public class List<T> : CollectionBase where t : IDisposable{

  public T this[int index]{

  public int Add(T value){return List.Add(value);}

  先行詞where的值可以是類,接口,結構,實作一個無參的公共構造器或有一個特定的基類的類。詳見有關幫助文檔。

  八、 總結

  泛型的設計是用來減少你重複實作的代碼的次數-隻需改變資料類型即可。因為抽象資料結構,如隊列,棧和清單皆是典型的資料結構,是以存在針對這些東西的泛型類完全可以了解。你可以從.NET中派生大量的值-通過使用現有的泛型類,如在System.Collections.Generic命名空間中的那些。

  可以肯定,在一段相當長的時間裡,泛型将會象模式和重構等革新一樣對開發帶來越來越大的價值,而且新的資料結構能被轉換成可重用的如泛型等的代碼元素。

繼續閱讀