在裝箱拆箱之前簡單說一下值類型、引用類型:
值類型:原類型(Sbyte、Byte、Short、Ushort、Int、Uint、Long、Ulong、Char、Float、Double、Bool、Decimal)、枚舉(enum)、結構(struct)等,是在棧中分配內存,在申明的同時就初始化,以確保數據不為NULL;
引用類型:類、數組、接口、委托、字符串等,在堆中分配內存,初始化為null,引用型是需要GARBAGE COLLECTION來回收內存的,值型不用,超出了作用范圍,系統就會自動釋放;
裝箱:將值類型轉換為引用類型,用於在垃圾回收堆中存儲值類型,是值類型到 object 類型或到此值類型所實現的任何接口類型的隱式轉換;
拆箱:將引用類型轉換為值類型,從 object 類型到值類型或從接口類型到實現該接口的值類型的顯式轉換;
利用裝箱和拆箱功能,可通過允許值類型的任何值與Object類型的值相互轉換,將值類型與引用類型鏈接起來
簡例:
int val = 100;
object obj = val;
Console.WriteLine (“對象的值 = {0}", obj);
這是一個裝箱的過程,是將值類型轉換為引用類型的過程
int val = 100;
object obj = val;
int num = (int) obj;
Console.WriteLine ("num: {0}", num);
這是一個拆箱的過程,是將值類型轉換為引用類型,再由引用類型轉換為值類型的過程
(注: .NET中,數據類型劃分為值類型和引用(不等同於C++的指針)類型,與此對應,內存分配被分成了兩種方式,一為棧,二為堆(托管堆)。值類型只會在棧中分配,引用類型分配內存與托管堆,托管堆對應於垃圾回收。)
1、 調用一個含類型為Object的參數的方法,該Object可支持任意為型,當你需要將一個值類型(如Int32)傳入時,則需要裝箱;
2、 一個非泛型的容器,同樣是為了保證通用,而將元素類型定義為Object。若要將值類型數據加入容器時,則需要裝箱;
裝箱:
1、 首先從托管堆中為新生成的引用對象分配內存;
2、 然後將值類型的數據拷貝到剛剛分配的內存中;
3、 返回托管堆中新分配對象的地址;
進行一次裝箱要進行分配內存和拷貝數據這兩項比較影響性能的操作。
拆箱:
1、 首先獲取托管堆中屬於值類型那部分字段的地址,這一步是嚴格意義上的拆箱;
2、 將引用對象中的值拷貝到位於線程堆棧上的值類型實例中;
嚴格意義上的拆箱,並不影響性能,但伴隨這之後的拷貝數據的操作就會同boxing操作中一樣影響性能。
理論上,裝箱時,生成的是全新的引用對象,這會有時間損耗,也就是造成效率降低。通過重載函數,也可以通過泛型來避免裝箱。
對於裝箱拆箱代碼的優化,由於C#中對裝箱和拆箱都是隱式的,所以,根本的方法是對代碼進行分析,而分析最直接的方式是了解原理結何查看反編譯的IL代碼。比如:在循環體中可能存在多余的裝箱,你可以簡單采用提前裝箱方式進行優化。