上一篇简单述说了值类型与引用类型的关系,那么不能不说说值类型的拆箱与装箱:
将值类型装箱就相当于把它包装起来给人看,"你瞧,我现在是引用类型了",顾名思义拆箱就是将包装卸下来"其实我是值类型"。
在将值类型利用装箱机制打包成引用时他要进行许多的打扮:
1.首先要给他一个空间(在托管堆中分配内存),多大呢,要必须要容得下包装所需要的物件化妆品之类的(各自段所需的内存量),此外还要有专家设计师吧(类型对象指针和同步块索引两个额外的成员);
2.空间有了,他就要开始将自己的东西和物件搬过来(只类型的字段复制到新分配的堆内存);
3.一切准备就绪,不过此时出了意外,他被人顶替冒充了,而冒充它的那个人很清楚这个房间的位置(返回对象地址,改地址就是对象引用)。
上面解释可能会有点牵强,给个例子一目了然:
1 struct Point{public int x,y;} 2 public sealed class Program{ 3 public static void Main() 4 { 5 ArrayList a = new ArrayList(); 6 Point p; 7 p.x=1;p.y=1; 8 a.Add(p); 9 } 10 }
最简单不过的代码了,由于Add()方法参数是Object类型,所以在执行时要进行一次装箱操作:Point值类型实例p中的字段会复制到新分配的Point对象中,已装箱的Point对象的地址返回并传给Add方法,需要说明的是Point对象此时是引用类型存在于堆中,Point值类型变量p可被重用,a已经不知道他的任何事情,由此可见已装箱值类型生存期超过了未装箱值类型的生存期。
-------------------------------------------------------------------------------
接下来说说拆箱:
紧接上段代码,如若Point p = (Point)a[0];代码执行时,就进行了一次拆箱:
一个人他知道了那个房间的地址,他把东西都要了回来,而东西可能已经变了(拆箱不是简单的装箱逆过程,它的代价比装箱要低,拆箱就是获取指针的过程,该指针指向包含在一个对象中的原始值类型(数据字段),指针指向的是已装箱实例中未装箱的部分,不要求在内存中复制任何字节);
拆箱时要注意:1.包含"对已装箱类型实例的引用"的变量为null时,会抛出异常(他都没有房间,也没有物件,你找个P啊)
2.如果引用的对象不是所需值类型的已装箱实例,抛出异常(张三的东西,李四来要,对方能给吗);
--------------------------------------------------------------------------------
下面来做个脑筋急转弯:
1 public static void Main(){ 2 int v=5; 3 Object o = v; 4 v=123; 5 Console.WriteLine(v+","+(int)o); 6 }
问:上面代码发生了多少次装箱?想好在打开。。。
三次,一次很好理解,剩下的两次,对比Console.WriteLine参数即可理解
部分内容参考自《CLR C#》