泛型是 C# 2.0 引入的核心特性,透過型別參數化實現程式碼重用與類型安全。本文從 IL 層面剖析泛型相較 ArrayList 的優勢,並深入探討泛型靜態成員的封閉型別隔離行為。
// ArrayList:裝箱、型別不安全、需轉型
var list1 = new ArrayList();
list1.Add(1); // 裝箱
list1.Add("a"); // 任意型別
int i = (int)list1[0] + 1; // 拆箱 + 強制轉型,運行期可能拋 InvalidCastException
// List<int>:無裝箱、型別安全、無轉型
var list2 = new List<int>();
list2.Add(1); // 正常
// list2.Add("a"); // 編譯錯誤
int j = list2[0] + 1; // 直接取值運算
List<int> 僅接受 int// ArrayList.Add(1) - 裝箱指令
IL_0009: box [mscorlib]System.Int32
IL_000e: callvirt ... ArrayList::Add(object)
// List<int>.Add(1) - 無 box 指令
IL_0037: callvirt ... List`1<int32>::Add(!0)
// ArrayList 讀取 - 拆箱
IL_0022: callvirt ... ArrayList::get_Item(int32) // 回傳 object
IL_0027: unbox.any [mscorlib]System.Int32 // 拆箱
// List<int> 讀取 - 直接回傳 int32
IL_003f: callvirt ... List`1<int32>::get_Item(int32) // 回傳 !0 (int32)
IL_0044: ldc.i4.1
結構函式差異:
// ArrayList
IL_0001: newobj ... ArrayList::.ctor()
// List<int> - 具體化型別參數
IL_002f: newobj ... List`1<int32>::.ctor()
T 為佔位符int) 產生機器碼,T 被實際型別取代List<int>, List<string>) 獨立產生原生碼(參考型別共享實作)關鍵規則:靜態成員在相同封閉型別的實例間共享,不同封閉型別互不共享。
public class Point<T> {
public static T Width;
public Point(T width) { Width = width; }
public override string ToString() => Width.ToString();
}
// 測試
var p1 = new Point<int>(1);
var p2 = new Point<int>(2);
var p3 = new Point<long>(3);
Console.WriteLine(p1); // 2 (p2 覆蓋 p1 的靜態 Width)
Console.WriteLine(p2); // 2
Console.WriteLine(p3); // 3 (Point<long> 獨立靜態 Width)
Point<int> 與 Point<long> 是完全不同的型別,各自擁有獨立的靜態 Width 欄位。
Func<T, TResult>, Action<T> 等預設委派IEnumerable<T>, IComparer<T> 等T Max<T>(T a, T b) where T : IComparable<T>where T : class/struct/new()/base/interface 限制型別參數out T (協變), in T (逆變) 用於介面/委派泛型透過 JIT 期具體化實現零成本抽象:編寫一次邏輯,獲得類型安全、效能最佳化、程式碼重用三重收益。理解其 IL 層面運作與靜態成員隔離規則,是進階 .NET 開發者的必修課。