當泛型型別或方法編譯為通用中繼語言 (CIL)時,它會包含元數據,將它識別為具有型別參數。 泛型型別的 CIL 使用方式會根據提供的型別參數是實值型別或參考型別而有所不同。
當泛型型別第一次以值型別作為參數建構時,執行階段會建立一個具體的泛型型別,並在 CIL 中適當位置替代提供的參數。 特製化泛型類型會針對做為參數的每個唯一實值型別建立一次。
例如,假設您的程式代碼宣告了以整數建構的堆疊:
Stack<int>? stack;
在此階段,運行時會生成一個特殊版本的Stack<T>類別,其中的整數會被適當替換為其參數。 現在,每當程式代碼使用整數堆疊時,運行時間就會重複使用產生的特製化 Stack<T> 類別。 在下列範例中,會建立兩個整數堆疊實例,並共用一個程式碼的 Stack<int>
單一實例:
Stack<int> stackOne = new Stack<int>();
Stack<int> stackTwo = new Stack<int>();
不過,假設另一個 Stack<T> 類別具有不同的實值類型,例如 long
或使用者定義結構,因為它的參數是在程式代碼的另一個點建立。 因此,執行時會生成泛型類型的另一個版本,並在 CIL 中的適當位置替換 long
。 轉換已不再需要,因為每個特製化泛型類別原生都包含實值型別。
泛型對於參考型別的運作方式有些不同。 第一次使用任何參考型別建構泛型類型時,運行時間會建立特製化的泛型型別,其對象參考會取代 CIL 中的參數。 然後,每當建構型別以參考型別具現化為其參數時,不論其類型為何,運行時間都會重複使用先前建立的泛型型別特製化版本。 這是可能的,因為所有參考的大小都相同。
例如,假設您有兩個參考型別:一個 Customer
類別和一個 Order
類別,另假設您建立了類型的 Customer
堆疊:
class Customer { }
class Order { }
Stack<Customer> customers;
此時,執行階段會產生Stack<T>類別的特殊版本,該版本會儲存稍後會填入的物件參考,而不是儲存數據。 假設下一行程式代碼會建立另一個參考類型的堆疊,其名稱為 Order
:
Stack<Order> orders = new Stack<Order>();
不同於實值型別,不會為Stack<T>型別建立另一個特殊版本的 Order
類別。 相反地,會建立類別特製化版本的 Stack<T> 實例,並將 orders
變數設定為參考它。 假設您接著遇到一行程式代碼來建立 Customer
類型的堆疊:
customers = new Stack<Customer>();
如同先前使用 Stack<T> 型別所建立的 Order
類別般,又創建了一個特製化的 Stack<T> 類別實例。 所包含的指標會設定為引用一個 Customer
類型大小的記憶體區域。 由於參考型別的數量可能會因程式而有很大的不同,C# 的泛型實作透過將編譯器為參考型別的泛型類別建立的特製化類別數量減少到一個,大幅減少程式碼的數量。
此外,當泛型 C# 類別使用實值型別或參考型別參數具現化時,反映可以在運行時間查詢它,而且可以確定其實際類型和類型參數。