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