CLR Via C#教程教你装箱和拆箱, 学了并将看过的部分写出来,因为写的过程也是一个加深理解的过程。本系列算是学习的一个记录吧,也可以方便以后自己查阅,如果对大家还有些帮助的话,我就很高兴了。书我是选择性的看的,所以顺序和书中的顺序可能不一样。
CLR Via C#教程装箱和拆箱是已经被嚼烂的两个概念了,并且在一些面试中也经常考到。
装箱:将值类型转换为引用类型。
拆箱:将引用类型转换为值类型。
值类型是一种相对轻型的类型,不像对象那样在托管堆中分配,也不会被GC,不通过指针来引用,不过在有些时候需要获取对值类型的引用,例如在使用net1.0的集合类ArrayList的时候。
- class Program
- {
- static void Main(string[] args)
- {
- ArrayList list = new ArrayList();
- Point p;//因Point为值类型,分配在堆栈中
- for (int i = 0; i < 100; i++)
- {
- pp.x = p.y = i; //初始化Point中的成员
- list.Add(p);//对p进行装箱后,将引用添加到list中
- }
- }
- }
- struct Point
- {
- public Int32 x;
- public Int32 y;
- }ArrayList的Add方法是接受一个Object参数,如下
- public virtual int Add(object value);
所以在执行Add方法时会将Point值类型转换为一个堆得托管对象,并获取到这个对象的引用,将引用地址存储在ArrayList中。
在一个值类型装箱的时候内部发生的事情:
l 在托管堆分配好内存。分配的内存是值类型的各个字段所需内存量加上托管堆上的两个额外成员(类型对象指针和同步索引块)所需的内存量。
l 值类型中的字段值复制到新分配的堆内存中。
l 返回对象的引用地址。
拆箱就是执行和装箱相反的操作,将引用类型转化为值类型。接上面的代码,获取ArrayList中的元素值用如下代码:
- for (int j = 0; j < 10; j++)
- {
- Point point =(Point)list[j];
- Console.WriteLine("X:" + point.x + " Y:" + point.y);
- }
上面的代码中通过索引取到ArrayList中存储的各个Point的引用地址,通过Point类型转换将其对应的值从堆中复制到Point的实例point中,这个转换的过程就是拆箱的过程。
在拆箱的过程中要注意以下两点:
1. 如果对已装箱的值类型的引用的变量为null,会引发NullRefreenceException异常
2. 如果一个引用指向的对象在拆箱时不是用的装箱时所使用的类型,将会引发InvalidCastException异常。代码如下:
- static void Main(string[] args)
- {
- Int32 x = 5;
- Object o = x;
- Int16 y = (Int16)o;//引发InvalidCastException异常
- }
正确的做法是,现将其用Int32类型来拆箱,然后再强制转换为Int16
- static void Main(string[] args)
- {
- Int32 x = 5;
- Object o = x;
- Int16 y = (Int16)(Int32)o;
- }
下面来看两段程序来深入理解下装箱和拆箱
代码一:
- static void Main(string[] args)
- {
- Int32 x = 5;
- Object o = x;
- x = 123;
- Console.WriteLine(x + ", " + (Int32)o);
- }
上面的代码中有多少次装箱呢?乍一看好像就一次(Object o=x;),其实一共有三次装箱,看看IL代码就一目了然了,以上就是CLR Via C#教程之一。
【编辑推荐】