C# 中的装箱和拆箱






3.19/5 (27投票s)
2002年5月2日
3分钟阅读

310867

1
使用 C# 进行装箱和拆箱的简介。
引言
装箱和拆箱是 .NET 类型系统中的一个重要概念。 通过装箱和拆箱,可以通过允许将值类型的任何值转换为类型对象或从类型对象转换,来链接值类型和引用类型。 装箱和拆箱能够实现类型系统的统一视图,其中任何类型的值最终都可以被视为一个对象。
将值类型转换为引用类型称为装箱。 拆箱是相反的操作,是一种显式操作。
.NET 提供了一个统一的类型系统。 所有类型,包括值类型,都派生自类型对象。 可以对任何值调用对象方法,甚至是原始类型(如 int)的值。
示例
using System;
class Test
{
static void Main() {
Console.WriteLine(3.ToString());
}
}
在整数文本上调用对象定义的 ToString 方法。
示例
class Test
{
static void Main() {
int i = 1;
object o = i; // boxing
int j = (int) o; // unboxing
}
}
一个 int
值可以被转换为对象,然后再次转换回 int。
这个例子展示了装箱和拆箱。 当需要将值类型的变量转换为引用类型时,将分配一个对象框来保存该值,并将该值复制到该框中。
拆箱正好相反。 当将对象框转换回其原始值类型时,该值将从框中复制出来并放入适当的存储位置。
装箱转换
装箱转换允许将任何值类型隐式转换为类型对象或值类型实现的任何接口类型。
装箱值类型的值包括分配一个对象实例并将值类型的值复制到该实例中。
例如,对于任何值类型 G,装箱类将被声明如下
class vBox
{
G value;
G_Box(G g) {
value = g;
}
}
现在,类型 G 的值 v 的装箱包括执行表达式 new G_Box(v),并将结果实例作为类型对象的值返回。
因此,语句
int i = 12;
object box = i;
在概念上对应于
int i = 12;
object box = new int_Box(i);
像上面这样的装箱类 G_Box
和 int_Box
实际上并不存在,并且装箱值的动态类型实际上不是类类型。 相反,类型 G 的装箱值具有动态类型 G,并且使用 is 运算符的动态类型检查可以简单地引用类型 G。例如,
int i = 12;
object box = i;
if (box is int) {
Console.Write("Box contains an int");
}
将在控制台上输出字符串 Box contains an int。
装箱转换意味着创建被装箱值的副本。 这与将引用类型转换为类型对象不同,在后一种情况下,该值继续引用相同的实例,并且仅被视为派生程度较低的类型对象。
例如,给定声明
struct Point
{
public int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
以下语句
Point p = new Point(10, 10);
object box = p;
p.x = 20;
Console.Write(((Point)box).x);
将在控制台上输出值 10,因为在将 p 赋值给 box 时发生的隐式装箱操作会导致 p 的值被复制。 如果 Point 被声明为一个类,则将输出值 20,因为 p 和 box 将引用相同的实例。
拆箱转换
拆箱转换允许从类型对象到任何值类型或从任何接口类型到实现该接口类型的任何值类型的显式转换。 拆箱操作包括首先检查对象实例是否是给定值类型的装箱值,然后将该值从实例中复制出来。
将对象 box 拆箱转换为值类型 G 包括执行表达式 ((G_Box)box).value
。
因此,语句
object box = 12;
int i = (int)box;
在概念上对应于
object box = new int_Box(12);
int i = ((int_Box)box).value;
为了使针对给定值类型的拆箱转换在运行时成功,源参数的值必须是对先前通过装箱该值类型的值创建的对象的引用。 如果源参数为空或对不兼容对象的引用,则会引发 InvalidCastException
。
结论
这种类型系统的统一性为值类型提供了对象性的好处,而不会引入不必要的开销。
对于不需要 int
值像对象一样运行的程序,int
值仅仅是 32 位的值。 对于需要 int
值表现得像对象的程序,这种功能可以按需使用。 这种将值类型视为对象的能力弥合了大多数语言中存在的值类型和引用类型之间的差距。