泛型是 C# 2.0 引入的核心特性,提供型別安全、效能(避免 boxing)、程式碼重用。本文說明決策依據、並以 StandardClass(非泛型)與 GenericClass<T>(泛型)並列對比,展示 CLR 為每個封閉建構型別產生獨立型別的機制。
決策檢核表:何時使用泛型
- 類別操作「未指定」的資料型別(集合、容器、工廠、比較器)。
- 處理值型別時要避免 boxing/unboxing。
- 需要編譯期強型別檢查,提早發現錯誤。
- 面臨「程式碼膨脹」:為每種型別寫近似類別(
StreamReaderList、StreamWriterList)。
- 追求程式碼清晰、可讀、可維護。
並列對比:StandardClass vs GenericClass<T>
// 非泛型:使用 object 陣列
public class StandardClass {
object[] _items;
public int AddItem(object item) { /* ... */ }
public object GetItem(int i) { /* ... */ }
}
// 泛型:型別參數 T
public class GenericClass {
T[] _items;
public int AddItem(T item) { /* ... */ }
public T GetItem(int i) { /* ... */ }
}
關鍵差異表
| 面向 | StandardClass | GenericClass<T> |
| 陣列型別 | object[] | T[] |
| AddItem 參數 | object | T |
| GetItem 回傳 | object | T |
| 型別安全 | 運行期拋 InvalidCastException | 編譯期錯誤(GenericClass.AddItem(1)) |
| 值型別效能 | 每次 Add/Get 發生 boxing/unboxing | 零 boxing,直接堆疊/內嵌儲存 |
| 取值轉型 | 需 (string)obj.GetItem(0),可能失敗 | 直接 string s = gen.GetItem(0),編譯保證 |
CLR 型別生成機制
// 程式碼
var a = new StandardClass(); // 1
var b = new StandardClass(); // 2
var c = new StandardClass(); // 3
var g1 = new GenericClass(); // 型別 1
var g2 = new GenericClass(); // 型別 2
var g3 = new GenericClass(); // 型別 3
var g4 = new GenericClass(); // 型別 3 (共用)
// 輸出靜態計數器
// StandardClass: 3 instances of StandardClass, items: Object[]
// GenericClass: 1 instance of GenericClass`1[Boolean], items: Boolean[]
// 1 instance of GenericClass`1[Int32], items: Int32[]
// 2 instances of GenericClass`1[String], items: String[]
結論:非泛型類別在 CLR 中只有一個 MethodTable;泛型類別每個「封閉建構型別」(如 GenericClass)都有獨立的 MethodTable、靜態欄位、JIT 代碼。這是泛型能提供零 boxing、型別安全的根本原因。
進階觀察
- 靜態欄位
_count 分屬各封閉型別,互不干擾。
typeof(GenericClass<>) 為開放型別(GenericClass`1),typeof(GenericClass) 為封閉型別。
- 泛型方法(非泛型類別的方法有型別參數)同樣享有上述優勢。
- 約束(
where T: ...)進一步縮小型別參數範圍,啟用成員存取。
理解泛型在 CLR 層面的運作,能幫助你在架構設計時果斷選擇泛型、正確預期效能與行為。
- 上一篇文章: C# 泛型集合遷移實戰:從 ArrayList 升級到 List<T>
- 下一篇文章: C# 介面實作深度解析:IComparable 與 IComparer 靈活排序策略
Copyright ©
程式碼之家 All Rights Reserved