首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

Effective C# 原則 17:最小化裝箱與拆箱操作

     編輯:關於C#

實值型別是資料容器,不支援多型;.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());

集合中的裝箱 Bug

.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" - 第三次拆箱

解決策略

  1. 優先使用不可變實值型別(參見原則 7)。
  2. 若需可變實值型別,改用 System.Array 保持型別安全。
  3. 透過介面存取盒子內部:實值型別實作介面,裝箱後的盒子也實作該介面,直接呼叫介面成員可修改盒內原始資料,避免拆箱。
    interface IPersonName { string Name { get; set; } }
    struct Person : IPersonName { ... }
    ((IPersonName)list[0]).Name = "New"; // 直接修改盒內資料
  4. C# 2.0 泛型集合(List<T> 等)徹底解決此問題(參見原則 49)。

時刻警惕

留意實值型別隱性轉為 System.Object、介面型別、集合元素、方法參數等場合。能避免就避免,必要時用 ToString()、泛型或介面技巧繞過。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved