C# 为什么结构需要装箱?

C# 为什么结构需要装箱?,c#,struct,boxing,C#,Struct,Boxing,在C#中,任何用户定义的struct自动成为System.structSystem.ValueType和System.structSystem.ValueType的子类 但当我们为对象类型引用指定某个结构时,它会被装箱。例如: struct A { public int i; } A a; object obj = a; // boxing takes place here 所以我的问题是:如果A是System.Object的后代,编译器不能将其向上转换为对象类型而不是装箱吗?结构

在C#中,任何用户定义的
struct
自动成为System.struct
System.ValueType
和System.struct
System.ValueType
的子类

但当我们为对象类型引用指定某个结构时,它会被装箱。例如:

struct A
{
    public int i;
}

A a;
object obj = a;  // boxing takes place here

所以我的问题是:如果
A
System.Object
的后代,编译器不能将其向上转换为对象类型而不是装箱吗?

结构是一种值类型<代码>系统。对象是引用类型。值类型和引用类型由运行时存储和处理。要将值类型视为引用类型,必须将其装箱。从低级的角度来看,这包括将值从它最初所在的堆栈复制到堆上新分配的内存,堆中还包含一个对象头。引用类型需要额外的头来解析其vtable,以启用虚拟方法分派和其他与引用类型相关的功能(请记住,堆栈上的结构只是一个值,它没有类型信息;它不包含任何类似vtables的内容,并且不能直接用于解析动态分派的方法). 此外,要将某个对象视为引用类型,必须有指向它的引用(指针),而不是它的原始值

所以我的问题是-如果A是System.Object的后代,编译器不能将其向上转换为对象类型而不是装箱吗


在较低级别,值不会继承任何内容。事实上,正如我之前所说,它不是一个真正的物体。A派生自
System.ValueType
,后者又派生自
System.Object
,这一事实是在编程语言(C#)的抽象级别定义的,C#确实对您隐藏了装箱操作。您没有明确提及任何内容来框显值,因此您可以简单地认为编译器已经为您“升级”了结构。它制造了值继承和多态性的幻觉,而多态行为所需的任何工具都不是由它们直接提供的。

struct
是一种设计上的值类型,因此在转换为引用类型时需要装箱
struct
源于
System.ValueType
,后者在术语上源于
System.Object


struct是object的后代这一事实并不意味着什么。因为CLR在运行时处理
struct
的方式与引用类型不同。

虽然.NET的设计者当然不需要包含第4.3节中的装箱,但这很好地解释了它背后的意图,IMO:

装箱和拆箱可以实现统一的 类型系统视图,其中 任何类型的值最终都可以 被当作物体对待

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


这与C++中的类型系统不统一,不同类型的公共基类型不同。

< P>这里我是如何思考的。考虑包含32位整数的变量的实现。当作为值类型处理时,整个值可放入32位存储器中。这就是值类型:存储只包含组成值的位,不多也不少

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

显然,这两件事是完全不同的

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

因此,在该场景中,您要做的是将整数框起来:创建一个包含整数存储的新对象,然后存储对新对象的引用


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

在回答问题后,我将介绍与该主题相关的一个小技巧:

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

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);
接口IFoo{…}
结构栏:IFoo{…}
无效装箱(ifoox){…}
void byValue(tx):其中T:IFoo{…}
var bar=新的bar();
拳击(酒吧);
byValue(巴);
“如果
结构A
System.Object
的后代,编译器不能向上转换它而不是装箱吗?”

不,仅仅是因为根据C语言的定义,在这种情况下,“上投”就是拳击

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