實值型別是資料容器,不支援多型;.NET 以單一繼承的參考型別 System.Object 為根。為連結兩者,框架引入裝箱(實值型別 → 參考型別,在堆上建立「盒子」存放副本)與拆箱(從盒子取回副本)。雖方便,但兩者是效能殺手,並可能產生隱藏 Bug,應儘量避免。
編譯器會在預期 System.Object 或介面型別處,自動產生裝箱/拆箱 IL,不發出警告。常見情境:
// 隱性裝箱三次
Console.WriteLine("Numbers: {0}, {1}, {2}", 25, 32, 50);
// 改為:預先 ToString 避免裝箱
Console.WriteLine("Numbers: {0}, {1}, {2}", 25.ToString(), 32.ToString(), 50.ToString());
.NET 1.x 非泛型集合(ArrayList 等)存取實值型別時必裝箱。修改取出元素的屬性,修改的是副本的副本,原集合元素不變:
struct Person { public string Name { get; set; } }
var list = new ArrayList();
list.Add(new Person { Name = "Old" });
var p = (Person)list[0]; // 拆箱產生副本
p.Name = "New"; // 修改副本
Console.WriteLine(list[0]); // 輸出 "Old" - 第三次拆箱
System.Array 保持型別安全。interface IPersonName { string Name { get; set; } }
struct Person : IPersonName { ... }
((IPersonName)list[0]).Name = "New"; // 直接修改盒內資料
List<T> 等)徹底解決此問題(參見原則 49)。留意實值型別隱性轉為 System.Object、介面型別、集合元素、方法參數等場合。能避免就避免,必要時用 ToString()、泛型或介面技巧繞過。