C#--裝箱和拆箱

我們都知道C#有裝箱和拆箱的操作,裝箱是把基本類型用它們相應的引用類型包裝起來,使其具有對象的性質;而拆箱則是將引用類型的對象簡化成值類型的數據。如:

int i=0;
object o = (object)i; //裝箱
i=(int)o;  //拆箱

轉換:

  裝箱 將值類型轉換成引用類型

  拆箱 將引用類型轉換成值類型

C#中,數據類型劃分爲值類型和引用類型,與此對應,內存分配被分成了兩種方式,一爲棧,二爲堆,注意:是託管堆。值類型只會在棧中分配,引用類型分配內存與託管堆。(託管堆對應於垃圾回收)

PS:o 和 i 的改變將互不影響,因爲裝箱使用的是 i 的一個副本。

對值類型在堆中分配一個對象實例,並將該值複製到新的對象中。
1:首先從託管堆中爲新生成的引用對象分配內存。 
2:然後將值類型的數據拷貝到剛剛分配的內存中。 
3:返回託管堆中新分配對象的地址,這個地址就是一個指向對象的引用了。
可以看出,進行一次裝箱要進行分配內存和拷貝數據這兩項比較影響性能的操作。

PS:o 和 i 的改變將互不影響。

1、首先獲取託管堆中屬於值類型那部分字段的地址,這一步是嚴格意義上的拆箱。
2、將引用對象中的值拷貝到位於線程堆棧上的值類型實例中。
經過這2步,可以認爲是同boxing是互反操作。嚴格意義上的拆箱,並不影響性能,但伴隨這之後的拷貝數據的操作就會同boxing操作中一樣影響性能。

對執行效率的影響:

顯然,從原理上可以看出,裝箱時,生成的是全新的引用對象,這會有時間損耗,也就是造成效率降低。 那該如何做呢? 
首先,應該儘量避免裝箱。 比如上例的兩種情況,都可以避免,在第一種情況下,可以通過重載函數來避免。第二種情況,則可以通過泛型來避免。 當然,凡事並不能絕對,假設你想改造的代碼爲第三方程序集,你無法更改,那你只能是裝箱了。 對於裝箱/拆箱代碼的優化,由於C#中對裝箱和拆箱都是隱式的,所以,根本的方法是對代碼進行分析,而分析最直接的方式是瞭解原理結合查看反編譯的IL代碼。比如:在循環體中可能存在多餘的裝箱,你可以簡單採用提前裝箱方式進行優化。

使用場景:

一般來說,裝箱操作主要用於這兩種場合:

1.賦值時使用:

在對Integer類型的變量直接賦值時會發生自動裝箱操作,將Integer對象賦值給int時會發生自動拆箱操作。當賦值大於127時,Integer每賦值一次都會產生一個新的Integer對象。

2.調用方法,傳遞對象參數類型時:

比如我們不能直接在集合中放入基本類型值,因爲集合只接收對象類型。我們可以將基本類型的值轉換成對象,然後將這些轉換的對象放入集合中。

再說一句:

目前在C#中,肯定不會再繼續用ArrayList來存儲一些對象的集合了,因爲有了一組新的泛型集合,例如用List<T>:

List<int> listInt=new List<int>();//聲明一個int類型的泛型集合
listInt.Add(1);//添加一條數據

在使用的時候就規定是什麼類型,在存取數據的時候,不需要再進行多餘的裝箱和拆箱操作。但是在寫代碼的時候還是會隱藏很多拆箱和裝箱的過程,注意儘量避免裝箱和拆箱的操作,如果不可避免,那就儘量減少裝箱和拆箱的操作,可以查看下方簡單示例(無實際意義):

ArrList arrayList =new ArrayList();
int number =1;//定義值類型

arrayList.Add(number);//三次添加會造成三次裝箱
arrayList.Add(number);
arrayList.Add(number);

object o =number;//提前進行一次裝箱操作
arrayList.Add(o);//三次添加並不會造成裝箱操作
arrayList.Add(o);
arrayList.Add(o);

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章