非泛型集合的集合問題
阿新 • • 發佈:2018-11-21
非泛型集合的使用例項
假設我們建立了非泛型的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