Person 為實值型別,存入 ArrayList 前被裝箱,產生副本。取出時透過屬性修改,又建立副本。修改僅針對副本,而 ToString() 存取 attendees[0] 中物件又是第三個副本。
正因如此,應建立不變實值型別(原則 7)。若非要在集合中使用可變實值型別,請用 System.Array(型別安全)。
若陣列非合理集合,可用介面修正。盡量選擇介面而非公用方法,存取箱子內部修改資料:
public interface IPersonName { string Name { get; set; } }
struct Person : IPersonName { /* 實作 */ }
// 使用:
ArrayList attendees = new ArrayList();
Person p = new Person("Old Name");
attendees.Add(p); // box
// 修改名稱:用介面,不需拆箱
((IPersonName)attendees[0]).Name = "New Name";
// 輸出 "New Name"
Console.WriteLine(attendees[0].ToString()); // unbox
裝箱後的引用型別會實現原資料型別所有已實作介面。無需副本,可透過呼叫箱子上的 IPersonName.Name 直接存取箱子內部實值型別資料。實值型別上建立介面可讓你存取集合中箱子內部,直接修改其值。實值型別實作介面不使其多型,仍引入裝箱懲罰(原則 20)。
C# 2.0 泛型介面與泛型集合會妥善處理集合與介面困境(原則 49)。在此之前,仍需避免裝箱。實值型別可隱式轉換為 System.Object 或任何介面引用,這些轉換隱式發生,發現困難。環境與語言規則導致裝箱拆箱在不知不覺中複製物件,產生 Bug;多型處理實值型別損失效能。時刻注意將實值型別放入集合、呼叫參數為 System.Object 的方法、強制轉換為 System.Object 的地方:能避免就盡量避免!