装箱
(boxing)
和拆箱
(unboxing)
是
C#
类型系统中重要的概念。它通过允许任何数值类型的数据被转换为任何形式类型的对象提供了数值类型和引用类型间的紧密联系。装箱和拆箱使得对在其中任何类型都可以最终被看作对象的类型系统的统一的观察变为可能。
装箱转换允许任何数值类型可以隐式地转换为
object
类型或任何由数值类型实现的接口类型。装箱一个数值类型的数据包括对对象实例的定位和把数值类型数据拷贝到那个实例中。
装箱数值类型的数据的实际过程,可以通过想像一个对那种类型的装箱类的实际例子来解释。对于数值类型
T
,装箱类要按下面定义:
class T_Box
{
T value;
{
T value;
T_Box(T t) {
value = t;
}
}
value = t;
}
}
对于类型
T
的数值
v
的装箱现在由执行表达式
T_Box(v)
来代替
,
并且返回类型为
object
的结果实例。这样,语句
int i = 123;
object box = i;
object box = i;
从概念上符合
int i = 123;
object box = new int_Box(i);
object box = new int_Box(i);
如上面的
T_Box
和
int_Box
的装箱类型实际不存在,而被装箱数据的动态类型实际上并不是一个类类型。作为替代,类型
T
的一个被装箱的数据有动态类型
T
,而使用
is
操作符的动态类型检查可以很方便地引用
T
。例如,
int i = 123;
object box = i;
if (box is int) {
Console.Write("Box contains an int");
}
object box = i;
if (box is int) {
Console.Write("Box contains an int");
}
将在控制台输出字符串
“
Box contains an int
”
。
装箱转换隐式地把被装箱的数据进行了备份。这与从引用类型到
object
类型的转换不同,在那里数据一直引用相同的实例,并被认为几乎不从类型
object
派生。例如,给出声明
struct Point
{
public int x, y;
{
public int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
this.x = x;
this.y = y;
}
}
下面的语句
Point p = new Point(10, 10);
object box = p;
p.x = 20;
Console.Write(((Point)box).x);
object box = p;
p.x = 20;
Console.Write(((Point)box).x);
因为在发生把
p
赋值给
box
的隐含装箱操作时,
p
被拷贝,所以将在控制台上输出数值
10
。如果
Point
被声明为一个类,因为
p
和
box
将引用相同的实例,就会输出
20
。
拆箱转换允许任何
object
类型或从任何由数值类型实现的接口类型,可以显式地转换为任何数值类型。一个拆箱操作由几部分动作组成,首先检查
object
实例是一个所给数值类型的被装箱数据,然后把数值从实例中拷贝出来。
参考前面章节描述的假象的装箱类型,从对象
box
到数值类型
T
的拆箱转换包括执行表达式
((T_Box)box).value
。这样,语句
object box = 123;
int i = (int)box;
int i = (int)box;
从概念上符合
object box = new int_Box(123);
int i = ((int_Box)box).value;
int i = ((int_Box)box).value;
对于为了在运行时提供数值类型的拆箱转换,源变量数据必须是一个指向一个早先对那个数值类型数据打包创建的对象。如果源变量是
null
或引用一个不相关的对象,就会抛出一个
InvalidCastException
错误。