装箱和拆箱






1.92/5 (7投票s)
2007 年 1 月 16 日
3分钟阅读

26157
装箱和拆箱
引言
在本文中,我将解释装箱和拆箱的概念。 C# 为我们提供了值类型和引用类型。 值类型存储在堆栈上,引用类型存储在堆上。 值类型到引用类型的转换称为装箱,将引用类型转换回值类型称为拆箱。
让我向您解释更多关于值类型和引用类型的信息。
值类型
值类型是直接映射到 FCL 的原始类型。 像Int32
映射到 System.Int32
, double
映射到 System.double
。 所有值类型都存储在堆栈上,所有值类型都派生自 System.ValueType
。 所有从 System.ValueType
派生的结构和枚举类型都在堆栈上创建,因此称为 ValueType
。
引用类型
引用类型在某些方面与值类型不同,即从堆中为它们分配内存。 所有类都是引用类型。 C#new
运算符返回对象的内存地址。
示例
让我们看一些例子,以便更好地理解值类型和引用类型。 因为我们知道所有 ValueTypes 都是从System.Value
派生的,我们可以这样写
System.ValueType r = 5;
那么你对上面的代码有什么看法。 它会编译吗? 是的,它会编译。 但是等等,它是什么类型引起的,我不记得任何类型叫做System.ValueType
,因为它是一个基类,所有值类型都从中继承。 那么它是 Int32
, Int64
, double
, decimal
等吗。 结果变量 'r' 的类型是 System.Int32
。 问题是为什么是 Int32
而不是 Int16
。 嗯,这是因为它默认映射到 Int32
,具体取决于变量的初始值。
你不能这样写,因为System.ValueType
不是原始类型,它是原始值类型的基类,这些数学运算可以在原始类型上执行。
System.ValueType r = 10;
r++;
在上面的例子中,我告诉你变量 'r' 将是System.Int32
变量,但如果你不相信我,你可以使用 GetType()
方法自己找到
System.ValueType r = 5;
Console.WriteLine(r.GetType()) // returns System.Int32;
这里有一些你可以自己尝试的例子

System.ValueType r = 23.45;
Console.WriteLine(r.GetType()); // what does this print
//-------------------------------------------------------
System.ValueType r = 23.45F;
Console.WriteLine(r.GetType()); // What does this print
//-------------------------------------------------------
System.ValueType r = 2U;
Console.WriteLine(r.GetType()); // What does this print
//-------------------------------------------------------
System.ValueType r = 'c';
Console.WriteLine(r.GetType()); // What does this print
//-------------------------------------------------------
System.ValueType r = 'ac';
Console.WriteLine(r.GetType()); // tricky
//-------------------------------------------------------
System.ValueType r = "Hello World";
Console.WriteLine(r.GetType()); // tricky
现在让我们跳到装箱。 有时我们需要将 ValueTypes 转换为 Reference Types,也称为装箱。 让我们看看下面的一个小例子。 你在例子中看到我写了“隐式装箱”,这意味着你不需要告诉编译器你要装箱Int32
到对象,因为它自己处理这个问题,尽管你总是可以像在隐式装箱之后看到的那样进行显式装箱。
Int32 x = 10;
object o = x ; // Implicit boxing
Console.WriteLine("The Object o = {0}",o); // prints out 10
//-----------------------------------------------------------
Int32 x = 10;
object o = (object) x; // Explicit Boxing
Console.WriteLine("The object o = {0}",o); // prints out 10
现在让我们看看将对象类型拆箱回值类型。 这是一个简单的代码,将对象拆箱回Int32
变量。 首先我们需要装箱它,这样我们才能拆箱。
Int32 x = 5;
object o = x; // Implicit Boxing
x = o; // Implicit UnBoxing
所以,你看到了装箱是多么容易,拆箱是多么容易。 上面的例子首先将Int32
变量装箱为对象类型,然后简单地将其拆箱回 x
。 所有转换都隐式地发生。 在这个例子中一切看起来都很正确,只有一个小问题,那就是上面的代码不会编译。 您不能隐式地将引用类型转换为值类型。 你必须像下面的代码中所示的那样显式地指定你要拆箱。
Int32 x = 5;
object o = x; // Implicit Boxing
x = (Int32)o; // Explicit UnBoxing
让我们看看另一个拆箱的小例子。
Int32 x = 5; // declaring Int32
Int64 y = 0; // declaring Int64 double
object o = x; // Implicit Boxing
y = (Int64)o; // Explicit boxing to double
Console.WriteLine("y={0}",y);
这个例子将不起作用。 它将成功编译,但在运行时,它将生成一个System.InvalidCastException
异常。 原因是变量 x 被装箱为 Int32
变量,所以它必须被拆箱为 Int32
变量。 所以,变量用于装箱的类型在拆箱同一个变量时将保持不变。 当然,你可以在将其拆箱为 Int32
之后将其转换为 Int64,如下所示
Int32 x = 5; // declaring Int32
Int64 y = 0; // declaring Int64 double
object o = x; // Implicit Boxing
y = (Int64)(Int32)o; // Unboxing and than casting to double
Console.WriteLine("y={0}",y);