淺C#中的裝箱和拆箱
1、什麼是裝箱和拆箱?
簡單的來說:
裝箱就是值型別轉換為引用型別;拆箱就是引用型別轉換為值型別
值型別,包括原型別(Sbyte、Byte、Short、Ushort、Int、Uint、Long、Ulong、Char、Float、Double、Bool、Decimal)、列舉 (enum) 、結構 (struct)。
引用型別,包括類、陣列、介面、委託、字串等
裝箱:值型別到引用型別或到此值型別所實現的任何介面型別的隱式轉換
例如: int temp = 3;
System.Object obj = temp;
其中,temp為值型別,在棧中分配;當分配obj這個引用型別時,我們需要在堆中分配一個obj物件,然後把temp值賦給它,這麼一系列的過程就是裝箱的過程。
拆箱:從引用型別到任意值型別的顯式轉換。與裝箱不同,拆箱式顯示轉換。
例如:int temp = 3;
System.Object obj = temp;
int i = (int) obj;
拆箱過程中,首先來確定物件obj為一個值型別的裝箱值,然後把值賦給值型別。
隱式轉換總會成功的情況,不會丟擲異常:
1、從派生類到基類;
2、從派生介面到基介面;
3、從類到介面(該類實現了介面);
4、從Null到任何類;
顯式引用轉換,以下可能丟擲異常,轉換不一定成功:
1、從基類到派生類;
2、從介面到介面(基介面到派生介面或者倆介面沒有關係);
3、從介面到類(該類實現了該介面或該類未封閉);
4、從類到介面(該類未實現該介面且該類未封閉);
2、上面簡單的介紹了拆箱和裝箱的定義,下面就來討論一下裝箱和拆箱與堆和棧怎樣使用
其中值型別是在棧中分配記憶體,本身的宣告就是一個初始化的過程,其不需要進行垃圾回收,只要超出所定義的作用範圍會自動釋放記憶體.
而引用型別則是在堆中分配的,和java一樣,在堆種分配記憶體,而其託管堆進行垃圾回收.
當兩種資料型別進行轉換時就引出了裝箱/拆箱.
3、拆箱和裝箱的優缺點
裝箱和拆箱雖然滿足了兩隻型別之間的轉換。但是從裝箱的過程中不難看出,每次裝箱時要在堆中new一個新的物件,當量特別大是肯定會大大影響程式的效率。事物總有兩面
性,every sword has two sides,事情便簡單了,效能也下來了。所以,在應用中,我們應該儘量避免裝箱操作。
瞭解了裝箱和拆箱的操作,我們可以清楚的明白:裝箱操作會導致資料在堆和棧上進行拷貝,頻繁的裝箱操作會效能損失。而相比而言拆箱過程對效能損耗還是比較小的。
4、裝箱和拆箱的詳細步驟
裝箱(box)的詳細步驟:
(1)、在堆上分配一個記憶體空間,大小等於需要裝箱的值型別物件的大小加上兩個引用型別物件都擁有的成員:型別物件指標和同步塊引用。
(2)、把堆疊上的值型別物件複製到堆上新分配的物件。
(3)、返回一個指向堆上新物件的引用,並且儲存到堆疊上被裝箱的那個值型別的物件裡。
這個步驟不需要程式設計師自己編寫,在任何出現裝箱的地方,編譯器會自動加上執行以上功能的IL程式碼。
所謂的拆箱就是裝箱對應著的概念,但拆箱的過程和裝箱並不是倒過來就是:
拆箱(unbox.any)的詳細步驟
如果為待拆箱物件為null,丟擲NullReferenceException異常。
如果引用指向的不是一個期望物件的已裝箱物件,丟擲InvalidCastException異常。
(1)、獲取已裝箱物件中各個欄位的地址,這個過程就是“拆箱”
需要說明的是一般拆箱以後會伴隨著物件的拷貝,但拷貝操作已經不是拆箱的範疇。
5、下面舉兩個小的例子來實現什麼是裝箱什麼是拆箱以及怎樣避免因頻繁的裝箱而耗費記憶體
(1)、裝箱:
using System;publicclass Test{publicstaticvoid Main(String[] args){int i = 10;//將值型別的i裝箱//需要注意的是:這裡的裝箱採用的是值的拷貝object obj = i;//檢驗是否裝箱成功了if(obj isint){Console.WriteLine("資料已經裝箱!");}//我們在這裡更改i的值i = 33;Console.WriteLine("int i現在的值是:{0}",i);Console.WriteLine("int i裝箱的值是:{0}",obj);}}(2)、拆箱:
int i = 10;object obj = i;int j = (int)obj;
(3)、避免頻繁的裝箱:
int i = 10;
int j = 20;
int s = 30;
Console.WriteLine("i的值為{0},j的值為{1},s的值為{2}", i.ToString(), j.ToString(), s.ToString());
.