在2005年底微軟公司正式發布了C# 2.0,與C# 1.x相比,新版本增加了很多 新特性,其中最重要的是對泛型的支持。通過泛型,我們可以定義類型安全的數 據結構,而無需使用實際的數據類型。這能顯著提高性能並得到更高質量的代碼 。泛型並不是什麼新鮮的東西,他在功能上類似於C++的模板,模板多年前就已 存在C++上了,並且在C++上有大量成熟應用。
本文討論泛型使用的一般 問題,比如為什麼要使用泛型、泛型的編寫方法、泛型中數據類型的約束、泛型 中靜態成員使用要注意的問題、泛型中方法重載的問、泛型方法等,通過這些使 我們可以大致了解泛型並掌握泛型的一般應用,編寫出更簡單、通用、高效的應 用系統。
什麼是泛型
我們在編寫程序時,經常遇到兩個模塊的功 能非常相似,只是一個是處理int數據,另一個是處理string數據,或者其他自 定義的數據類型,但我們沒有辦法,只能分別寫多個方法處理每個數據類型,因 為方法的參數類型不同。有沒有一種辦法,在方法中傳入通用的數據類型,這樣 不就可以合並代碼了嗎?泛型的出現就是專門解決這個問題的。讀完本篇文章, 你會對泛型有更深的了解。
為什麼要使用泛型
為了了解這個問題 ,我們先看下面的代碼,代碼省略了一些內容,但功能是實現一個棧,這個棧只 能處理int數據類型:
public class Stack
{
private int[] m_item;
public int Pop(){...}
public void Push(int item){...}
public Stack(int i)
{
this.m_item = new int[i];
}
}
上面代碼運行的 很好,但是,當我們需要一個棧來保存string類型時,該怎麼辦呢?很多人都會 想到把上面的代碼復制一份,把int改成string不就行了。當然,這樣做本身是 沒有任何問題的,但一個優秀的程序是不會這樣做的,因為他想到若以後再需要 long、Node類型的棧該怎樣做呢?還要再復制嗎?優秀的程序員會想到用一個通 用的數據類型object來實現這個棧:
public class Stack
{
private object[] m_item;
public object Pop(){...}
public void Push(object item){...}
public Stack(int i)
{
this.m_item = new[i];
}
}
這個 棧寫的不錯,他非常靈活,可以接收任何數據類型,可以說是一勞永逸。但全面 地講,也不是沒有缺陷的,主要表現在:
當Stack處理值類型時,會出現 裝箱、折箱操作,這將在托管堆上分配和回收大量的變量,若數據量大,則性能 損失非常嚴重。
在處理引用類型時,雖然沒有裝箱和折箱操作,但將用 到數據類型的強制轉換操作,增加處理器的負擔。
在數據類型的強制轉 換上還有更嚴重的問題(假設stack是Stack的一個實例):
Node1 x = new Node1();
stack.Push(x);
Node2 y = (Node2)stack.Pop ();
上面的代碼在編譯時是完全沒問題的,但由於Push了一個 Node1類型的數據,但在Pop時卻要求轉換為Node2類型,這將出現程序運行時的 類型轉換異常,但卻逃離了編譯器的檢查。
針對object類型棧的問題, 我們引入泛型,他可以優雅地解決這些問題。泛型用用一個通過的數據類型T來 代替object,在類實例化時指定T的類型,運行時(Runtime)自動編譯為本地代 碼,運行效率和代碼質量都有很大提高,並且保證數據類型安全。