然而,如果立即實例化 Dictionary 類而且提供的類型參數沒有實現 IComparable 接口,則程序將遇到運行時錯誤,尤其是 InvalidCastException 異常。
聲明約束
在 C# 中,程序可以為泛型類中聲明的每個類型參數提供可選約束列表。約束表示要將一個類型構造成泛型所必須滿足的要求。可以使用 where 關鍵字聲明約束,該關鍵字後跟“參數-要求”對,其中“參數”必須是泛型中定義的某個參數,“要求”必須是類或接口。
為了滿足在 Dictionary 類中使用 CompareTo 方法的需要,程序可以對 KeyType 類型參數添加約束,要求傳遞給 Dictionary 類作為第一個參數的任何類型都必須實現 IComparable 接口,例如:
public class Dictionary<KeyType, ValType> where KeyType : IComparable
{
public void Add(KeyType key, ValType val)
{
...
switch(key.CompareTo(x))
{
}
...
}
}
這樣,編譯代碼時就會檢查代碼,以確保程序每次使用 Dictionary 類時,作為第一個參數傳遞的類型都實現了 IComparable 接口。此外,程序在調用 CompareTo 方法之前,再也無需將變量顯式轉換為 IComparable 接口了。
多重約束
對於任何給定的類型參數,程序可以為其指定任意多個接口約束,但最多只能指定一個類約束。每個新約束都以另一個“參數-要求”對的形式進行聲明,並且給定的泛型的每個約束都用逗號分隔。以下示例中的 Dictionary 類包含兩種參數,KeyType 和 ValType。KeyType 類型參數有兩個接口約束,而 ValType 類型參數有一個類約束:
public class Dictionary<KeyType, ValType> where
KeyType : IComparable,
KeyType : IEnumerable,
ValType : Customer
{
public void Add(KeyType key, ValType val)
{
...
switch(key.CompareTo(x))
{
}
...
}
}
運行時的泛型
泛型類的編譯方法與常規類的編譯方法幾乎沒有差別。事實上,編譯結果只不過是元數據和中間語言 (IL)。當然,為了接受代碼中用戶提供的類型,應對 IL 進行參數化。根據提供的類型參數是值類型還是引用類型,泛型的 IL 的用法會有所不同。
當將值類型作為參數首次構造泛型時,運行時將使用提供的參數替換 IL 中的相應位置來創建一個專用的泛型。針對每個用作參數的唯一值類型,將一次性創建專用的泛型。
例如,假設程序代碼聲明了一個由整數構造的 Stack:
Stack<int> stack;
此時,運行時將生成一個專用的 Stack 類,並用整數替換此類的相應參數。現在,無論程序代碼何時使用整數 Stack,運行時都將重復使用生成的專用 Stack 類。以下示例將創建整數 Stack 的兩個實例,每個實例均使用由此整數 Stack 的運行時所生成的代碼來創建:
Stack<int> stackOne = new Stack<int>();
Stack<int> stackTwo = new Stack<int>();
但是,如果在程序代碼中的其他位置又創建了一個 Stack 類,並使用不同的值類型(例如長整型或用戶定義的結構)作為其參數,則運行時將生成其他形式的泛型,而這時會替換 IL 相應位置中的長整型參數。為使用值類型構造的泛型創建專用類的優點是可以獲得更好的性能。畢竟每個專用的泛型類都是在“本地”包含值類型,因此不必再進行轉換。