裝箱:
值類型比引用類型“輕”,原因是他們不作為物件在託管堆中分配,不被垃圾回收,也不透過指標進行引用。但許多時候都需要取得值類型的參考,例如,假定要建立ArrayList物件來容納一組point結構,程式碼如下:
public sealed class Program
{
#public static void Main()
{
ArrayList a = new ArrayList();
Point p; //指派一個Point(不在堆疊中指派)
for (int i = 0; i < 10; i++)
{
p.x = p.y = i; //初始化值類型中的成員
#a.Add(p); //對值型別裝箱,將引用加入ArrayList
}
}
}
每次循環迭代都初始化一個ponit的值類型字段,並將該point儲存到ArrayList。但思考一下ArrayList究竟儲存了什麼?是Point結構,Point結構的位址,還是其他完全不同的東西?要知道正確答案,必須研究ArrayList的Add方法,並了解他的參數被定義成哪種類型。本例的Add方法原型如下:
public virtual int Add(Object value);
可以看出Add取得的是一個Object參數,也就是說,Add取得對託管堆上的一個物件的引用來作為參數。但之前的程式碼傳遞的是p,也就是一個Point,是值型別。為了使程式碼正確運作,Point值類型必須轉換成真正的,在堆中託管的對象,而且必須取得對該對象的參考。
將值型別轉換成參考型別要使用裝箱機制。下面說說對值類型的實例進行裝箱時發生的事情:
1,在託管堆中分配記憶體。分配的記憶體量是值型別各欄位所需的記憶體量,還要加上託管堆所有物件都有的兩個額外成員(型別物件指標和同步區塊索引)所需的記憶體量
2,值類型的欄位複製到新分配的堆記憶體
3,傳回物件位址。現在該物件是物件參考;值類型成了引用類型
C#編譯器偵測到上述程式碼是向要求引用類型的方法傳遞值類型,所以自動產生程式碼會對物件進行裝箱。所以在執行時,目前存在於Point值類型實例p中的欄位複製到新指派的Point物件中。已裝箱Point物件(現在是引用型別)的位址傳回並傳送給Add方法。 Point物件一直存在於堆中,直到被垃圾回收。 Point值類型變數p可重複使用,因為ArrayList不知道關於他的任何事情。在這種情況下,已裝箱類型的生存期超過了為裝箱值類型的生存期。
拆箱:
假設要用程式碼取得ArrayList的第一個元素:
Point p=(Point)a[0] ;
他取得ArrayList的元素0包含的引用,視圖將其放到Point值類型的實例p中。為此,已裝箱Point物件中的所有欄位都必須複製到值類型變數中,後者在執行緒堆疊上。 CLR分兩步驟完成複製。第一步取得已裝箱Point物件中的各個Point欄位位址。這個過程稱為拆箱。第二部將欄位包含的值從堆疊複製到基於堆疊的值類型實例中
#拆箱不是直接將裝箱過程倒過來。拆箱的程式碼比裝箱低很多。拆箱其實就是取得指標的過程,該指標指向包含在一個物件中的原始值類型。其實,指標指向的是已裝箱實例中的未裝箱部分。所以和裝箱不同,茶香不要求在記憶體中複製任何字節,知道這個重要區別後,還應知道的一個重點是,往往緊接著一次字段複製。
已裝箱值類型實例在拆箱是,內部發生下面這些事情:
1,如果包含「對已裝箱值類型的參考」的變數為null,拋出NullReferenceException異常
2,如果引用的物件不是所需值類型的已裝箱實例,拋出InvalidCastException異常
第二條表示程式碼的工作方式和你想的可能不一樣:
public static void Main()
{
Int32 x = 5;
##Object o = x; //對x裝箱,o引用已裝箱物件
Int16 y = (Int16)o; //拋出InvalidCastException異常
}
##從邏輯上說,完全能取得o引用的已裝箱Int32,將其強制轉換為int16.但是對物件進行拆箱時,只能轉型為最初未裝箱的值類型--本例Int32,以下是正確寫法:
public static void Main()
{
Int32 x = 5;
Object o = x; //對x裝箱,o引用已裝箱物件
Int16 y = (Int16)(Int32)o; //先拆箱為正確類型,再轉型
##}
在看以下程式碼:
public static void Main()
{
Point p;
p.x = p.y = 1;
Object o = p; //對p裝箱,o引用已裝箱實例
//將Point的x字段變成2
p = (Point)o; //對o拆箱,將欄位從已裝箱的實例複製到堆疊變數中
p.x = 2; //更改堆疊變數的狀態
o = p; //將p裝箱,o引用新的已裝箱實例
}
#最後三行程式碼的唯一目的就是將Point的x字段從1變成2.為此,首先要執行一次拆箱,在執行一次字段複製,再更改字段(棧上),最後執行一次裝箱(在託管堆上創建全新的已裝箱實例)。由此可以看出裝箱拆箱對應用程式效能的影響。
問:
public static void Main()
{
Int32 v = 5;
Object o = v;
v = 123;
Console.WriteLine(v+","+(Int32)o);
}
上述程式碼發生了多少次裝箱?
以上是裝箱與拆箱的實例教程的詳細內容。更多資訊請關注PHP中文網其他相關文章!