为什么结构需要装箱?

时间:2009-12-30 05:43:25

标签: c# struct boxing

在C#中,任何用户定义的struct自动成为 System.Struct System.ValueType System.Struct {{1}的子类。 }是System.ValueType的子类。

但是当我们为对象类型引用分配一些结构时,它会被装箱。例如:

System.Object

所以我的问题是:如果struct A { public int i; } A a; object obj = a; // boxing takes place here A的后代,编译器不能将它上传到对象类型而不是装箱吗?

6 个答案:

答案 0 :(得分:37)

结构是一种值类型。 System.Object是引用类型。运行时以不同方式存储和处理值类型和引用类型。要将值类型视为引用类型,必须将其设置为加框。从低级别的角度来看,这包括将值从最初所在的堆栈复制到堆上新分配的内存,该内存还包含一个对象头。引用类型需要额外的头来解析它们的vtable以启用虚拟方法调度和其他引用类型相关的功能(请记住,堆栈上的结构只是一个值而且它没有类型信息;它不包含任何类似vtable和can的内容不能直接用于解析动态调度的方法。此外,要将某些东西视为引用类型,您必须有一个引用(指针),而不是它的原始值。

  

所以我的问题是 - 如果A是System.Object的后代,是不是可以将它编译为对象类型而不是拳击?

在较低级别,值不会继承任何内容。实际上,正如我之前所说,它并不是一个真正的对象。 A派生自System.ValueType而后者派生自System.Object的事实是在您的编程语言(C#)的抽象级别定义的东西,C#确实很好地隐藏了拳击操作。您没有明确提及任何内容来设置值,因此您可以简单地认为编译器已为您“上传”了该结构。它使值的继承和多态的幻觉,而多态行为所需的工具都不是由它们直接提供的。

答案 1 :(得分:17)

这是我更喜欢考虑它的方式。考虑包含32位整数的变量的实现。当被视为值类型时,整个值适合32位存储。这就是值类型:存储只包含构成值的位,仅此而已。

现在考虑包含对象引用的变量的实现。该变量包含一个“引用”,可以通过多种方式实现。它可以是垃圾收集器结构的句柄,也可以是托管堆上的地址,或者其他什么。但它可以让你找到一个对象。这就是引用类型:与引用类型变量关联的存储包含一些允许引用对象的位。

显然,这两件事完全不同。

现在假设你有一个object类型的变量,并且你希望将int类型变量的内容复制到其中。你怎么做呢?构成整数的32位不是这些“引用”之一,它只是一个包含32位的桶。引用可以是进入托管堆的64位指针,也可以是垃圾收集器数据结构中的32位句柄,或者您可以想到的任何其他实现,但32位整数只能是32位整数。

那么你在那个场景中做的就是装整数:你创建一个包含整数存储的新对象,然后存储对新对象的引用。

只有在您希望(1)具有统一类型系统,以及(2)确保32位整数消耗32位内存时才需要拳击。如果你愿意拒绝其中任何一个,那么你就不需要拳击;我们不愿意拒绝那些,所以拳击是我们被迫忍受的。

答案 2 :(得分:4)

虽然.NET的设计者当然不需要包含C# Language Specification的拳击部分4.3,但很好地解释了它背后的意图,IMO:

  

拳击和拆箱可实现统一   其中a。类型系统的视图   任何类型的价值最终都可以   作为对象对待。

因为值类型不是引用类型(最终是System.Object),所以存在装箱行为以便拥有统一类型系统,其中任何的值可以表示为对象

这与C ++不同,其中类型系统不统一,所有类型都没有通用的基本类型。

答案 3 :(得分:0)

struct是一种设计的值类型,因此在转换为引用类型时需要加框。 struct来自System.ValueType,其术语来自System.Object

struct 对象的后代这一事实并不意味着......因为CLR在运行时以不同于引用类型的方式处理structs

答案 4 :(得分:0)

问题解答后,我会提出与该主题相关的一些“技巧”:

struct可以实现接口。如果将值类型传递给期望此值类型实现的接口的函数,则该值通常会被装箱。使用泛型可以避免装箱:

interface IFoo {...}
struct Bar : IFoo {...}

void boxing(IFoo x) { ... }
void byValue<T>(T x) : where T : IFoo { ... }

var bar = new Bar();
boxing(bar);
byValue(bar);

答案 5 :(得分:0)

  

“如果struct ASystem.Object的后代,编译器是否不能将其上传而不是装箱?”

不,仅仅因为根据C#语言的定义,在这种情况下“up-casting”拳击。

C#的语言规范包含(在第13章中)所有可能类型转换的目录。所有这些转化都按特定方式进行分类(例如数字转换,参考转换等)。

  1. 从类型S到其超类型T存在隐式类型转换,但这些转换仅针对类类型{{1}中的模式“定义}到引用类型S。由于您的T不是类类型,因此无法在您的示例中应用这些转换。

    也就是说,struct A(间接地)从A派生(虽然正确)这一事实在这里无关紧要。相关的是object是结构值类型。

  2. 现有转化,其匹配模式“从值类型A到其参考超类型A被归类为拳击转换。因此,从objectstruct的每次转化都是按照定义被视为装箱。