1. 程式人生 > >非泛型集合的集合問題

非泛型集合的集合問題

非泛型集合的使用例項

  假設我們建立了非泛型的System.Collections.ArrayList來儲存數值(分配在棧上的)資料,查詢ArrayList成員,就會發現它們所操作的是System.Object資料:

public class ArrayList:...
{
  ...
  public virtual int Add(object value);
  public virtual void Insert(int index,object value);
  public virtual void Remove(object obj);
  public virtual
object this[int index] { get;set;} }

  ArrayList所操作的是object,這表示資料分配在堆上,然而下面的程式碼不但可以通過編譯,而且執行時也不會丟擲錯誤:

static void WorkWithArrayList()
{
  //在傳遞給需要object的方法時,值型別會自動裝箱
  ArrayList myInts = new ArrayList();
  myInts.Add(10);
  myInts.Add(20);
  myInts.Add(35);
}

  儘管把數字資料(值型別)直接傳入要求object的方法,但執行時會自動將棧資料進行裝箱。
然後,如果希望從ArrayList中獲取項,則必須使用轉換操作,將堆分配的物件拆箱成棧分配的整數。

static void WorkWithArrayList()
{
  //在傳遞給需要object的方法時,值型別會自動裝箱
  ArrayList myInts = new ArrayList();
  myInts.Add(10);
  myInts.Add(20);
  myInts.Add(35);

  //當將object轉換回棧資料時,會發生拆箱
  int i = (int)myInts[0];//注意,ArrayList的索引器返回System.Object,不是System.Int32;

  //由於WriteLine()要求object型別,因此再次發生裝箱操作
  Console.WriteLine("Value of your int:{0}"
,i); }

裝箱/拆箱帶來的問題

  裝箱和拆箱帶來的堆/棧記憶體轉移會導致效能問題,並且也缺乏型別安全。

型別安全問題

  其中對於型別安全問題,因為拆箱必須回到合適的資料型別,如果拆箱為不正確的變數將丟擲異常。為了確保安全,你需要將每個拆箱操作都包裹在try/catch邏輯中,這樣工作量大並且也帶來了效能問題。

static void SimpleBoxUnboxOperation()
{
  //建立一個ValueType(int)變數
  int myInt = 25;

  //將int裝箱為object引用
  object boxedInt = myInt;

  //拆箱為錯誤的資料型別將觸發執行時異常
  try //為保證型別安全,大量使用try/catch將導致效能問題
  {
      long unboxedInt = (long)boxedInt;
  }
  catch(InvalidCastException ex)
  {
      Console.WriteLine(ex.Message);
  }
}

效能問題

除了try/catch可能帶來的效能問題,思考一下在裝箱和拆箱一個整數時發生的步驟,具體如下:
1. 必須在託管堆上分配一個新物件
2. 基於棧資料的值必須被轉移到新分配的記憶體位置
3. 在拆箱時,儲存在堆物件中的值必須轉移回棧
4. 堆上無用的物件(最後)會被回收

理想情況下,我們應該可以在沒有任何效能問題的容器中操作棧資料,並且在獲取資料時也不必使用try/catch作用域(這正是泛型所實現的)

參考《精通C#(第六版)》章節9.2