.NET 通用類型系統(CTS)將所有類型分為兩大類:
System.ValueType,包含基本型別(int, double 等)、結構體、列舉。特點:儲存在堆疊/內嵌、不可為 null、傳值語意、無多型行為。System.Object,包含類別、介面、委派、陣列。特點:儲存在堆積、可為 null、傳引用語意、支援多型。這種二元結構造成「原生型別非物件」的缺陷,導致無法統一處理。Java 採用包裝類別,C++ 採用模板/多重繼承,.NET 則創造性地引入裝箱/拆箱機制,實現值類型與引用類型的無縫轉換。
.locals init ([0] float64 dubBox, [1] object objBox)
IL_0000: ldc.r8 77.77 // 推入常數
IL_0009: stloc.0 // 存入區域變數 dubBox (值類型)
IL_000a: ldloc.0 // 載入值類型
IL_000b: box [mscorlib]System.Double // 裝箱指令!關鍵步驟
IL_0010: stloc.1 // 存入 objBox (引用類型)
box 指令接收值類型的型別標記,執行上述三步驟。裝箱後的物件擁有完整的 System.Object 行為:可呼叫 ToString()、GetHashCode()、作為 object 參數傳遞、存入集合等。
NullReferenceException。InvalidCastException。unbox.any [mscorlib]System.Double // 拆箱並轉換
// 或分兩步:
unbox [mscorlib]System.Double // 取得資料區位址
ldobj [mscorlib]System.Double // 複製值
| 操作 | 配置 | 複製 | GC 壓力 |
|---|---|---|---|
| 裝箱 | Heap 配置 | 位元複製 | 增加 Gen0 壓力 |
| 拆箱 | 無配置 | 位元複製 | 無直接壓力 |
頻繁裝箱(如非泛型集合 ArrayList.Add(1)、介面呼叫 IComparable.CompareTo(1)、字串格式化 string.Format("{0}", 1))會產生大量短命物件,觸發 Gen0 GC,影響效能。
List<int> 替代 ArrayList,IComparable<T> 替代 IComparable。$"{value}" 內部仍會裝箱,熱路徑可用 value.ToString() 手動控制。int CompareTo(MyStruct other) 避免拆箱。值類型呼叫繼承自 Object 的虛擬方法(ToString, Equals, GetHashCode)時,不會裝箱,JIT 直接發出 call 指令。但透過介面或 object 參數呼叫則必須裝箱。
裝箱/拆箱是 .NET 統一類型系統的基石,讓值類型在需要時擁有引用類型身分。理解其 IL 機制、效能代價與避免策略,是寫出高效能 .NET 程式碼的關鍵。現代開發中,善用泛型與 Span<T>、Memory<T> 等新型別,可將裝箱減至最低。