装箱和拆箱概念,给值类型和引用类型相互转换搭起了一座桥梁,即:value_type值可以转换成object类型的值,反过来也可以。
1、装箱操作:允许将vaule_type隐式地转换成reference_type。
装箱操作发生时,一般遵循下面步骤:创建一个对象实例,将value_type值复制到这个实例里面。
在C#语言规范中,有一个例子,可以拿来供大家理解:我们可以设想有一个泛型装箱类Box<T>,他的声明和行为如下:
sealed class Box<T> : ValueTuple { T value; public Box(T t) { this.value = t; } }
当我们进行下面一个常见的装箱操作时:
int a = 10; object box = a;
其实就可以当成:
int a = 10; object box_box = new Box<int>(a);
以上便是装箱操作的过程的拆解。我们要知道并不存在这样一个Box<T>装箱类。
关于装箱,我们要注意一点:装箱转换隐含着“复制”概念。我们常见的从引用类型referece_type到object类型的转换,可不是装箱转换。
下面的例子也是装箱操作,从它的输出可以看出,值类型和引用类型,转换前后其实没有关系了。
struct MyPoint { public int x, y; public MyPoint(int a,int b) { this.x = a; this.y = b; } } private static void Main(string[] args) { MyPoint myPoint = new MyPoint(10,2); object point = myPoint; myPoint.y = 20; Console.WriteLine(((MyPoint)point).y); }
输出结果:2;
因为:mypoint是值类型,它赋值给point时是一种装箱操作。mypoint数值在栈内存中复制了一份放到了堆中point的实例中。操作mypoint的属性对于point对象再无任何影响。假如把MyPoint声明为class类型,那么输出结果就是20,因为此时mypoint和point引用的是同一实例。
2、拆箱操作:也称取消装箱操作,它将reference_type类型的值显示转换成了value_type类型的值。
拆箱操作发生时经历一下步骤:首先检查实例中是否存在给定的值类型的值,然后把这个值从实例中复制出来。
参照在装箱操作中假想的装箱类,以下普通的拆箱操作:
object o = 10; int y = (int)o;
便等同于:
object box = new Box<int>(10); int yy = ((Box<int>)box).value;//从实例中找到int类型的值value,复制给变量yy中
可以看出:拆箱操作,经历了将值从堆内存中搬运到栈内存中的一个过程。
总结:
无论是装箱操作还是拆箱操作,都涉及到数据在堆和栈之间进行来回搬运,这对程序性能是一种损耗。所以在编码过程中尽量避免装箱和拆箱操作。