在 C# 程式設計中,「裝箱」與「拆箱」是初學者容易混淆、也是資深開發者必須掌握的核心概念。它們關係到值類型與參考類型之間的轉換,更直接影響程式的記憶體配置與執行效能。本文將從定義、底層運作機制、優缺點分析,到實際程式碼範例,帶你徹底理解這對「雙生概念」。
object 或介面類型)。| 值類型 | 參考類型 |
|---|---|
| 基本型別 (int, double, bool 等)、列舉、結構 | 類別、陣列、介面、委派、字串 |
值類型變數直接儲存資料,配置在堆疊上。生命週期隨作用域結束自動釋放,無須垃圾回收介入,存取速度極快。
參考類型變數儲存的是記憶體位址,實際物件配置在受控堆積上,由 GC 管理生命週期。
當 int val = 3; object obj = val; 執行時:
obj。這就是「裝箱」的完整過程——隱性發生、配置記憶體、複製資料。
編譯器會自動在 IL 碼中插入 box 指令,開發者無需手寫。
NullReferenceException 或 InvalidCastException)。對應 IL 指令為 unbox.any。注意:拆箱後通常伴隨複製,但複製動作不屬於拆箱範疇。
// ❌ 產生裝箱
object obj = 10;
Console.WriteLine(obj.ToString());
// ✅ 使用介面或泛型避免裝箱
IFormattable fmt = 10; // 介面轉換仍會裝箱,但泛型不會
Console.WriteLine(fmt.ToString());
// ✅ 最佳:直接使用值類型方法
int i = 10;
Console.WriteLine(i.ToString()); // 呼叫 int 覆寫的 ToString,無裝箱
// ArrayList 存值類型會裝箱
ArrayList list = new ArrayList();
list.Add(100); // 裝箱
// 建議使用泛型集合
List<int> genericList = new List<int>();
genericList.Add(100); // 無裝箱
// 字串格式化隱性裝箱
string s = string.Format("{0}", 100); // 100 被裝箱
// 改用插值字串 (C# 6+) 或 ToString
string better = $"{100}"; // 仍會裝箱,但語法更清晰
string best = 100.ToString(); // 無裝箱
using System;
public class BoxingDemo
{
public static void Main()
{
int val = 10;
object boxed = val; // 裝箱
Console.WriteLine($"原始值: {val}");
Console.WriteLine($"裝箱後: {boxed}");
Console.WriteLine($"是否為 int: {boxed is int}");
val = 33; // 修改原值
Console.WriteLine($"修改後原始值: {val}");
Console.WriteLine($"裝箱值不變: {boxed}"); // 證明是複製而非共用
int unboxed = (int)boxed; // 拆箱
Console.WriteLine($"拆箱結果: {unboxed}");
}
}
// ❌ 每次 ToString 都裝箱
Console.WriteLine("值: {0}", i.ToString());
// ✅ 直接傳值類型,Console.WriteLine 內部處理
Console.WriteLine("值: {0}", i);
// ✅ 使用插值字串 (編譯器優化)
Console.WriteLine($"值: {i}");
ToString() 直接呼叫等技巧消除不必要裝箱。理解裝箱/拆箱機制,不僅能寫出高效能程式碼,更能在除錯時精準定位記憶體異常。希望本文能幫你建立清晰的記憶體模型認知。