装箱 拆箱
裝箱(boxing):將值類型轉換為引用類型。
拆箱(unboxing):將引用類型轉換為值類型。
c#數(shù)據(jù)類型分:值類型【簡單類型(布爾類型 字符類型 實數(shù)類型)?結構類型struct??枚舉類型enum】?,?引用類型【接口類型interface??所有的數(shù)組?類類型class?委托delegate】??指針類型
?
?
在拆箱的過程中要注意以下兩點:
1. 如果對已裝箱的值類型的引用的變量為null,會引發(fā)NullRefreenceException異常
2. 如果一個引用指向的對象在拆箱時不是用的裝箱時所使用的類型,將會引發(fā)InvalidCastException異常。代碼如下:
?
1 static void Main(string[] args)
2 {
3 Int32 x = 5;?
4 Object o = x;?
5 Int16 y = (Int16)o; //引發(fā)InvalidCastException異常
6 }
7
正確的做法是,現(xiàn)將其用Int32類型來拆箱,然后再強制轉換為Int16
?
1 static void Main(string[] args)
2 {
3 Int32 x = 5;?
4 Object o = x;?
5 Int16 y = (Int16)(Int32)o;?
6 }
?
下面來看兩段程序來深入理解下裝箱和拆箱
代碼一:
1 static void Main(string[] args)
2 {
3 Int32 x = 5;?
4 Object o = x;?
5 x = 123;?
6?
7 Console.WriteLine(x + ", " + (Int32)o);?
8 }
9
上面的代碼中有多少次裝箱呢?乍一看好像就一次(Object o=x;),其實一共有三次裝箱,看看IL代碼就一目了然了。
?
程序的執(zhí)行步驟:
?
1 創(chuàng)建一個Int32的未裝箱的值類型實例x,并初始化為5.
2 創(chuàng)建Object類型的變量o,并指向x。由于引用類型的變量必須要執(zhí)行堆中的對象,所以要對x進行裝箱(第一次裝箱),并將x在堆中的引用地址存儲在o中。
3 將值123賦給未裝箱的值類型實例x中。
4 調用WriteLine方法,WriteLine方法的參數(shù)值類型為String,現(xiàn)在WriteLine方法存在三個數(shù)據(jù)項,值類型x、string類型“,”和一個已裝箱的Int32類型實例的引用o,這三個數(shù)據(jù)項必須要合并成一個string對象才能被調用。
5 調用String對象的靜態(tài)方法Concat,Concat方法有9個重載,根據(jù)那三個數(shù)據(jù)項會選擇下面方法執(zhí)行。
?
?
6 第一個參數(shù)arg0傳入的是x ,參數(shù)類型為object,所以要對x進行裝箱(第二次裝箱),將引用地址傳給arg0,arg1傳入的是字符串“,”,字符串就是引用類型,直接傳引用地址,arg2傳入的是將o拆箱然后再裝箱(第三次裝箱)的引用地址傳入。
?
上面代碼中的WriteLine方法如果直接寫成Console.WriteLine(x + ", " + o); 將會有跟高的相率,因為o本身就是Object類型,在Concat的時候不用進行裝箱拆箱。
?
?
代碼二:看看這段程序發(fā)生了幾次裝箱
?
1 static void Main(string[] args)
2 {
3 Int32 x = 5;?
4 Object o=x;?
5 x=123;?
6 Console.WriteLine(x);?
7 x = (Int32)o;?
8 Console.WriteLine(x);?
9 Console.WriteLine(o);?
10 }
11
上面的代碼只發(fā)生了一次裝箱,因為WriteLine方法的重載版本中參數(shù)類型可以為Objet或是Int32,在調用WriteLine方法是并沒有裝箱,唯一的一次裝箱是Object o=x; 。
?
代碼三:
1 static void Main(string[] args)
2 {
3 Int32 x = 5;?
4 CheckRef(x, x); //輸出不同引用
5 }
6?
7 static void CheckRef(object obj1, object obj2)
8 {
9 if (obj1 == obj2)
10 Console.WriteLine("相同引用");?
11 else
12 Console.WriteLine("不同引用");?
13 }
14
?
1 static void Main(string[] args)
2 {
3 Int32 x = 5;?
4 Object o = x;?
5 CheckRef(o,o); //輸出相同引用
6 }
7?
8 static void CheckRef(object obj1, object obj2)
9 {
10 if (obj1 == obj2)
11 Console.WriteLine("相同引用");?
12 else
13 Console.WriteLine("不同引用");?
14 }
15
執(zhí)行上面代碼將發(fā)生兩次裝箱,因為CheckRef方法的兩個參數(shù)都是Object類型,傳入的都是值類型的實例,可以講代碼改進下,先將x轉換成Object類型再傳入方法,如下:
?
改進后只進行一次裝箱操作了,效率提高了,但是會發(fā)現(xiàn)運行的結果頁發(fā)生了變化,所以這種做法在有些時候是很危險的。
裝箱拆箱操作極大的破環(huán)程序的性能,不過在Net2.0中提供了泛型集合類,所以完全可以用List 和Dictionary 來代替 原來1.0中的ArrayList和HashTable,即使是List也會比ArrayList的性能要好。
總結
- 上一篇: 面试的27个经典问题
- 下一篇: Android借助Application