在c#语言中有两种类型,分别是引用类型和值类型,由于值类型不在托管堆中分配、不被垃圾回收机制印象、同样也不需要引用(指针)来引用,因此可能看上去,值类型要比引用类型更加轻巧,但是有的时候,我们所需要的是一个引用,而不是值类型,如下例
void Start()
{
ArrayList array = new ArrayList();
Vector3 vector;
for(int i = 0;i< 3;i++)
{
vector.x = 1;
vector.y = 2;
vector.z = 3;
array.Add(vector);
}
}
如果查看ArrList类的add方法可以发现add方法的参数是一个引用类型的变量,但是显然vector是一个值类型的变量,而不是引用,因此在这种时候,必须将值类型转换为引用类型,这就涉及到了装箱
装箱步骤
1.在托管堆中分配内存。需要注意的是,由于是将值类型进行引用类型话,因此在分配内存空间除了值类型需要的各个字段所需的内存之外,还要再加上托管堆说有对象都有的两个额外成员(类型对象和同步索引块)所需的内存
2.将值类型的字段赋值到新分配的堆内存中
3.返回对象地址,即对象引用,至此,值类型变成引用类型
结合上面的例子可以看出,在装箱过程中,当C#编译器检测到array.add,这行代码是向要求引用类型的方法传递值类型时,会自动生成代码对V进行装箱,在运行时,当前v的字段中储存的值会被赋值到新分配的对象中去,以装箱的Vector3对象(认为时引用类型)的地址会被返回给add方法,而装箱后的对象会一直存在于托管堆中,直至被垃圾回收。
拆箱,如下
Vector3 vector3 = vector[0];
这行便是执行了拆箱操作,需要注意的是,在拆箱中必须要告诉编译器,要拆成什么类型。具体过程如下
1.获取已经装箱的Vector3对象中各个字段的地址,这个过程便是所说的拆箱
2.将已经装箱的Vector3对象中各个字段的值从托管堆上复制到线程栈的新的值类型vector3上去
注意不要把拆箱当作装箱的逆过程,拆箱并不涉及到复制的过程。而是复制过程,相比于装箱,拆箱的代价要小得多,而且拆箱的目标类型一定要是未装箱的值类型;由于装箱和拆箱、复制会堆程序的速度和内存空间产生不利的影响,甚至可能会由于频繁的操作托管堆导致增加GC次数,因此,装箱拆箱次数越少越好,日常开发中一般使用List< T >而不是ArrayList这种会触发装箱机制的类型