object)。配置堆疊記憶體,複製值,回傳參考。本質:值型別與參考型別統一型別系統 的橋樑。所有型別 (含 int, struct, enum) 最終繼承自 System.Object。
// 原始碼
int val = 42;
object boxed = val; // 裝箱
int unboxed = (int)boxed; // 拆箱
.method private hidebysig static void Main() cil managed
{
.entrypoint
.maxstack 2
.locals init (
[0] int32 val,
[1] object boxed,
[2] int32 unboxed
)
IL_0000: ldc.i4.s 42 // 推入常數 42
IL_0002: stloc.0 // 存入 val (stack slot 0)
IL_0003: ldloc.0 // 載入 val
IL_0004: box [mscorlib]System.Int32 // ★ 裝箱指令:配置物件、複製值、推回參考
IL_0009: stloc.1 // 存入 boxed
IL_000a: ldloc.1 // 載入 boxed
IL_000b: unbox.any [mscorlib]System.Int32 // ★ 拆箱指令:型別檢查 + 複製值
IL_0010: stloc.2 // 存入 unboxed
IL_0011: ret
}
| 指令 | 作用 | 備註 |
|---|---|---|
box <type> | 值型別 → 物件參考 | 配置在 Managed Heap,包含 SyncBlockIndex、MethodTable 指標、值資料 |
unbox <type> | 物件參考 → 值型別位址 (&) | 回傳 managed pointer (&),需配合 ldobj 讀取值 |
unbox.any <type> | 物件參考 → 值型別 (直接複製值) | C# (T)obj 編譯為此指令,等同 unbox + ldobj |
struct Point : IComparable { public int X, Y; public int CompareTo(Point other) => X.CompareTo(other.X); }
Point p = new Point { X = 1 };
IComparable cmp = p; // ★ 裝箱!介面參考指向箱化副本
cmp.CompareTo(new Point()); // 在箱化副本上呼叫,原結構體未被修改
int i = 100;
string s = i.ToString(); // ★ 裝箱:呼叫 object.ToString() 虛方法
string s2 = $"{i}"; // ★ 裝箱:string.Format 參數為 object
Console.WriteLine("{0}", i); // ★ 裝箱:參數陣列 object[]
var list = new ArrayList();
list.Add(10); // ★ 裝箱
list.Add(20);
int sum = (int)list[0] + (int)list[1]; // ★ 拆箱
void Log(params object[] args) { }
Log(1, 2, 3); // ★ 三次裝箱,配置 object[3]
// BenchmarkDotNet 簡易測試
[Benchmark] public void NoBoxing() { int sum = 0; for (int i = 0; i < 1000; i++) sum += i; }
[Benchmark] public void WithBoxing() { object sum = 0; for (int i = 0; i < 1000; i++) sum = (int)sum + i; }
// 典型結果 (視 CPU/GC 模式):
// NoBoxing : ~ 2,000 ns, 0 B allocated
// WithBoxing : ~ 45,000 ns, ~ 24 KB allocated (1000 次裝箱/拆箱)
// ❌ ArrayList, Hashtable
// ✅ List, Dictionary, HashSet
var list = new List { 1, 2, 3 }; // 零裝箱
// ❌ void Process(object obj) { }
// ✅ void Process(T item) where T : IEquatable { }
public interface IRepository { void Add(T entity); } // 避免 object
// 呼叫端
public static T Max(T a, T b) where T : IComparable => a.CompareTo(b) > 0 ? a : b;
// 內部 CompareTo 為結構體方法,無裝箱
// 避免
Console.WriteLine("{0}", i);
// 改用 (C# 10+ 內插字串處理器可零配置)
Console.WriteLine(i.ToString()); // Int32.ToString() 非虛擬,無裝箱
// 或
Console.WriteLine($"{i}"); // 現代編譯器優化,可能避免裝箱
ValueTask<T> 避免非同步方法的類別配置。Span<T>, Memory<T> 避免陣列切片配置。// 原始:每次 WriteLine 都裝箱
Console.WriteLine("Val: {0}, Unboxed: {1}", val, unboxed); // 2次裝箱
// 優化:預先裝箱一次
object oVal = val;
object oUnboxed = unboxed;
Console.WriteLine("Val: {0}, Unboxed: {1}", oVal, oUnboxed); // 0次額外裝箱
int vs long, enum 底層型別)。InvalidCastException。null 拆箱拋出 NullReferenceException。裝箱/拆箱是 C# 型別系統統一性的代價。理解 IL 機制、識別隱性發生點、善用泛型與現代型別,能在不犧牲表達力的前提下,將配置壓力降至最低。**輪廓原則:預設用泛型,值型別遠離 object,熱路徑用 Span/ValueTask。**