使用引用类型变量进行对象类型装箱

16

当值类型被赋值给对象类型时,就会发生装箱(boxing)。那么当引用类型被赋值给对象时,是否也是一样呢?

当一个非对象类型被赋值时会发生什么?这也算是装箱吗?

    int num=5;
    object obj = num;  //boxing
    //////////////////////
    MyClass my = new MyClass();
    object obj = my; //what is name this convert  (whethere is boxing?)

就像 Henk 所说的,所有类型都是对象,而引用类型无论如何都存储在堆上。 - Jodrell
5个回答

29

"装箱"(Boxing)是指将值类型转换为对象类型。

不太准确。当值类型的值转换引用类型时,才会发生“装箱”。

如果将引用类型的值分配给对象类型的变量,情况是否相同?

不相同。“装箱”仅在将值类型的值转换为引用类型时发生。将引用类型的值转换为对象不是“装箱”转换,而是引用转换。

如果将非对象引用类型的值分配给对象类型的变量,会发生什么?

引用类型的值是一个引用。当引用分配给对象类型的变量时,在与变量关联的存储位置中复制引用。

"那也算装箱吗?"

不是的。只有当值类型的值被转换为引用类型时才会发生装箱。将引用类型的值转换为对象不是“装箱”转换,而是引用转换。


如果没有数据/类型转换涉及,也就是在赋值过程中没有需要/调用“转换运算符”的情况下,比如object obj = new MyClass();,仍然被认为是一种“转换”吗? - samus
1
@sαmosΛris:这是一种隐式引用转换 - Eric Lippert

14

我假设您的意思是:

string s = "hello";
object x = s;        // no boxing, just implict conversion to base-type.

这段代码可行的原因是System.String像所有其他的类一样,派生自System.Object

public sealed class String : Object { ... }

3

装箱是在堆栈上创建一个对象引用,该引用引用堆上的某种类型的值,例如int。但是当将引用类型(不是对象)分配给对象时,它不会装箱。


3
为什么引用必须在堆栈中?引用不能在堆中或寄存器中吗? - Eric Lippert

2
Eric的答案对应CLI(公共语言基础结构)标准ECMA-335,第I部分(架构),第5章(术语和定义),其中将装箱定义为:“将某个值类型的值转换为新分配的引用类型System.Object的实例”,并将拆箱定义为:“将类型为System.Object且其运行时类型为值类型的值转换为值类型实例。”
CIL(公共中间语言)的box和unbox指令的行为就是这样,而在C#或VB.NET上下文中谈到装箱/拆箱时通常也是这个意思。
然而,“装箱”和“拆箱”这些术语有时会以更广泛/实用的意义被使用。例如,F# box和unbox操作符可以将值类型和引用类型转换为System.Object,并从System.Object转换回来:
> let o = box "Hello World";;
val o : obj = "Hello World"
> let s:string = unbox o;;
val s : string = "Hello World"

0

编译提供的代码以生成可执行文件并对其进行反汇编,可以发现第一个赋值(obj)中存在一个明确的盒子指令,而第二个赋值(obj2)中不存在:

来源

namespace BoxingAndTypeConversion
{
    class Program
    {
        public class MyClass { }

        static void Main(string[] args)
        {
            int num = 5;
            object obj = num;  //boxing
            //////////////////////
            MyClass my = new MyClass();
            object obj2 = my; //what is name this convert  (whethere is boxing?)
        }
    }
}

CIL

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       19 (0x13)
  .maxstack  1
  .locals init ([0] int32 num,
           [1] object obj,
           [2] class BoxingAndTypeConversion.Program/MyClass my,
           [3] object obj2)
  IL_0000:  nop
  IL_0001:  ldc.i4.5
  IL_0002:  stloc.0
  IL_0003:  ldloc.0
  IL_0004:  box        [mscorlib]System.Int32
  IL_0009:  stloc.1
  IL_000a:  newobj     instance void BoxingAndTypeConversion.Program/MyClass::.ctor()
  IL_000f:  stloc.2
  IL_0010:  ldloc.2
  IL_0011:  stloc.3
  IL_0012:  ret
} // end of method Program::Main

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接