官方指南翻译
官方指南: Boxing and Unboxing (C# Programming Guide)
装箱是把一个值类型转换为object
类型或转换为任何实现了该值类型接口的类型的过程.当CLR装箱一个值类型时,它将这个值封装进一个System.Object并且存在堆中.拆箱将这个值从这个object中取出来.装箱是隐式的,拆箱是显式的.装箱和拆箱的概念是C#类型系统统一视图的基础.C#类型系统中,一任何类型的值都可以被当做object.
在下面的例子里,int值i
被装箱,并赋值给objecto
1 | int i = 123; |
然后objecto
可以拆箱并赋值给变量i
1 | o = 123; |
下面的例子解释了装箱在C#中的应用
1 | // String.Concat 例子 |
性能
因为涉及到一个简单的分配, 所以装箱和拆箱在计算上是昂贵的. 当一个值类型被装箱时, 一个新的object就会被指派和创建. 在较小的程度上, 拆箱需要的花费在计算上也是很昂贵. 更多信息请浏览性能
装箱
装箱用来将值类型存到垃圾回收(GC)堆中. 装箱隐式地将值类型转换为object
类型或转换为任何实现了该值类型接口的类型. 将一个值类型装箱会在堆中指派一个object实例并将这个值拷贝进这个新的object.
考虑下下面这个值类型变量的声明:
1 | int i = 123; |
下面的语句隐式地对变量i
进行了拆箱:
1 | // 拆箱将i的值拷贝进object o |
上面语句的结果是,在栈上创建一个对象o
的引用, 引用了在堆上的int
类型的值. 这个值是一个分配到变量i
上的值类型的拷贝. i
和o
这两个变量的区别可以用下面这张装箱变换的图来形象的解释:
装箱也可以像下面这样显式的执行, 不过显式的装箱永远不会用到:
1 | int i = 123; |
描述
这个例子用装箱将一个int变量i
转换成一个objecto
. 然后,如果存放在变量i
中的值从123
变为456
… 下面的例子表明了,圆本
的值类型和装箱后的object使用不同的内存地址, 所以可以存储不同的值.
例子
1 | class TestBoxing |
拆箱
拆箱显式地将object
类型或任何实现了该值类型接口的类型转换为一个值类型. 一个拆箱操作由下面两部分组成:
- 确定这个object实例是一个给定值类型的装箱.
- 将这个实例中的值拷贝给一个值类型变量.
下面的语句展示了装箱和拆箱操作:
1 | int i = 123; // 一个值类型 |
下面的图是上面语句的演示:
为了确保拆箱在运行时能成功, 被拆箱的东西必须是一个之前被装箱创建的object的引用. 尝试去拆箱null
会引发一个NullReferenceException.尝试拆箱一个不相容的类型会引发一个InvalidCastException
例子
下面的例子示范了一个不正确的引发了InvalidCastException
的拆箱的例子:
1 | class TestUnboxing |
上面的程序会输出:
Specified cast is not valid. Error: Incorrect unboxing.
如果你将int j = (short) o;
改为int j = (int) o;
, 这个转换就会成功, 会输出Unboxing OK.