65.9K
CodeProject 正在变化。 阅读更多。
Home

装箱和拆箱

starIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIconemptyStarIcon

1.92/5 (7投票s)

2007 年 1 月 16 日

3分钟阅读

viewsIcon

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); 
© . All rights reserved.