分析 C# 2.0 新特性 -- 範型(Generics)
作者:梁振[MS-MVP]
範型是提高物件導向程式多態性設計衍生的。
1,C# 多態性設計回顧和展望
在引入範型這個概念之前,回顧一下1.0或1.1中關於的Object類型的定義:
Object類型是.NET Framework中System.Object的一個別名,可以分配任何類型給Object類型的變數。
通過object類型的引入,實現了.NET對於物件導向程式多態設計。
因為Object本身是一個參考型別,是存放在Heap(堆)上的。對於其他參考型別和Object轉換很容易實現。
而實值型別和Object類型轉換需要引入兩個用於object類型和實值型別轉換概念boxing和unboxing.
(1)boxing 裝包
將轉換一個實值型別到Object類型,
例如:int i = 1; //儲存在棧上
object o = (object) i;//儲存在堆上
上面的代碼在IL中將會是box [mscorlib]System.Int32
將Int32和1同時裝入一個Object對象中,結構如下:
object[{Int32}-{1}];//前者表示boxing類型,後者表示其值
(2)unboxing 拆包
將一個已裝包為Obect類型的實值型別轉換回實值型別
操作分為兩部分組成:
a,首先檢查是否轉換回的類型是裝包的類型,如果不是拋出一個InvalidCastException執行階段錯誤.
b,複製Object類型中的值到目標實值型別變數;
例如:
int i = 1;
object o = (object) i; //boxing
int j = (int)o //unboxing
//double d = (double)o ,出現執行階段錯誤
通過上面的可以看到在.NET Framework 1.0 中使用object對象設計的多態性比C++的template(一種基於類似宏的編
譯時替換)執行效率增加了大量的copy的開銷。所以在.NET Framewrok 2.0中引入範型來提高高物件導向程式多態性
設計。
2,範型概念和特點:
範型的設計是為瞭解決上面提到過的Object的多態性設計中的兩個問題:
(1),效能上面的,boxing和unboxing需要大量的複製開銷;
(2),安全性上面的,在上面一個例子看到了如果unboxing類型不同會拋出一個InvalidCastException異常;
範型的設計格式是使用<和>封閉其中一個範型參數,例如:
public class Stack<T>;
範型的執行個體化格式是使用需要使用的類型替換<和>封閉其中一個範型參數,例如:
Stack<char> char_Stack = new Stack<char>();
多範型類定義格式,在<和>封閉多個範型參數,例如:
class Node<K,T>
對於C++程式員,看上面關於範型的格式很快聯絡到了ISO C++當中的Template;
的確兩者文法上面非常相似,但是兩者的多態性編譯和實現有很大不同.
在C++的Template編譯後,沒有編譯帶有Template的代碼.而是通過一種宏的方式進行的替換過程.
每次使用Template類型,編譯器都會產生一個對應的類型代碼.而不管是否個類型代碼已經使用過了.
在C#2.0中範型是在中繼語言(IL)和通用語言執行平台(CLR)支援的.
對於實值型別:會在JIT編譯時間候替換參數類型,如果存在以及編譯特定類型的機器代碼,將直接返回這段代碼.
這樣避免了在ISO C++中Template可能帶來代碼膨脹.
對於參考型別:會直接在JIT編譯時間候替換參數類型.
理解C# 2.0範型是在實現是基於CLR支援的很重要啊,因為.NET的本質是和語言無關的.任何語言最後都是編譯為中
間語言,這樣基於IL和CLR支援的範型可以運用到所有基於CLR實現的語言,例如:Visual Basic 2005等等.
3,範型和其他類型執行效率對比例子
下面分別是使用Int,Object和範型構造的3個棧的類
/// <summary>
/// Int類型實現的棧
/// </summary>
class IntStack
{
private int[] data;
private int current;
private int length;
public IntStack(int Length)
{
length = Length;
current = 0;
data = new int[length];
}
public int Top()
{
return data[current - 1];
}
public void Push(int Data)
{
if (current < length)
{
data[current++] = Data;
}
}
public void Pop()
{
if (current > 0)
{
current--;
}
}
}
/// <summary>
/// 範型的棧
/// </summary>
/// <typeparam name="T">範型</typeparam>
class TemplateStack<T>
{
private int length;
private int current;
private T[] data;
public TemplateStack(int Length)
{
current = 0;
length = Length;
data = new T[length];
}
public T Top()
{
return data[current - 1];
}
public void Push(T Data)
{
if (current < length)
{
data[current++] = Data;
}
}
public void Pop()
{
if (current > 0)
{
current--;
}
}
}
/// <summary>
/// Object的棧
/// </summary>
class ObjectStack
{
private object[] data;
private int current;
private int length;
public ObjectStack(int Length)
{
length = Length;
current = 0;
data = new object[length];
}
public object Top()
{
return data[current-1];
}
public void Push(object Data)
{
if (current < length)
{
data[current++] = Data;
}
}
public void Pop()
{
if (current > 0)
{
current--;
}
}
}
通過測試直接使用Int的棧和範型構造Int棧的開銷接近.
而比較前面兩個Object每增加一次unboxig開銷是增加的2倍.
4,附錄.NET 2.0 Framework 範型容器列表:
Comparer<T> Comparer 比較
Dictionary<K,T> HashTable hash表
LinkedList<T> LinkList 鏈表
List<T> ArrayList 數組鏈表
Queue<T> Queue 隊列
SortedDictionary<K,T> SortedList 排序鏈表
Stack<T> Stack 棧
ICollection<T> ICollection 容器介面
IComparable<T> System.IComparable比較介面
IDictionary<K,T> IDictionary 字典介面
IEnumerable<T> IEnumerable 枚舉介面
IEnumerator<T> IEnumerator 跌代介面
IList<T> IList 鏈表介面
參考資料:
《Design and Implementation of Generics for the .NET Common Language Runtime》
---Andrew Kennedy Don Syme (Microsoft Research, Cambridge, U.K.)
《An Introduction to C# Generics》
---Juval Lowy IDesign (MSDN Online)
《C# Programmer's Reference》
---MSDN Library (MSDN Online)