ValueTypes如何从Object(ReferenceType)派生而仍然是ValueTypes?

Joa*_*nge 78 .net c# clr value-type reference-type

C#不允许从类派生结构,但所有ValueType都派生自Object.这种区别在哪里?

CLR如何处理这个问题?

Eri*_*ert 103

C#不允许从类派生结构

你的陈述是不正确的,因此你的困惑.C#确实允许结构从类派生.所有结构派生自同一个类System.ValueType,它派生自System.Object.所有枚举都来自System.Enum.

更新:一些(现已删除)评论中存在一些混淆,需要澄清.我会问一些其他问题:

结构是否来自基类型?

显然是的.我们可以通过阅读规范的第一页看到这一点:

所有C#类型(包括int和double等基本类型)都从单个根对象类型继承.

现在,我注意到规范夸大了这里的情况.指针类型不是从对象派生的,并且接口类型和类型参数类型的派生关系比此草图指示的更复杂.但是,显然所有结构类型都是从基类型派生的.

还有其他方法我们知道结构类型派生自基类型吗?

当然.结构类型可以覆盖ToString.什么是覆盖,如果不是它的基类型的虚方法?因此它必须具有基本类型.那个基类型是一个类.

我可以从我选择的类派生用户定义的结构吗?

显然没有.这并不意味着结构不是来自一个类.结构派生自一个类,从而继承该类的可遗传成员.事实上,结构需要从一个特定的类派生出来:需要枚举Enum,结构需要派生自ValueType.因为这些是必需的,所以C#语言禁止您在代码中声明派生关系.

为什么要禁止呢?

当需要关系,语言设计者可以选择:(1)要求用户键入所需的咒语,(2)使其成为可选项,或(3)禁止它.每个都有优点和缺点,C#语言设计师根据每个的具体细节选择不同.

例如,const字段必须是静态的,但是禁止说它们是因为这样做是第一次,毫无意义的措辞,其次,暗示存在非静态const字段.但是,即使开发人员别无选择,也需要将重载运算符标记为静态; 开发人员很容易相信运算符重载是一种实例方法.这超越了用户可能会认为"静态"意味着"虚拟"也是可能性的担忧.

在这种情况下,要求用户说他们的结构派生自ValueType似乎只是过多的措辞,并且它暗示结构可以从另一种类型派生.为了消除这两个问题,C#使得在结构派生自基类型的代码中声明是非法的,尽管它确实如此.

类似地,所有委托类型都派生自MulticastDelegate,但C#要求您要这样做.

所以,现在我们已经确定C#中的所有结构都来自一个类.

类的继承派生之间有什么关系?

许多人对C#中的继承关系感到困惑.继承关系非常简单:如果结构,类或委托类型D派生自类型B,那么B的可遗传成员也是D的成员.就这么简单.

当我们说结构派生自ValueType时,对继承有什么意义?简单地说,ValueType的所有可遗传成员也是结构的成员.例如,这就是结构体如何获得它们的实现ToString; 它继承自struct的基类.

所有遗产成员?当然不是.私人成员是否可以遗传?

是.基类的所有私有成员也是派生类型的成员.如果呼叫站点不在该成员的可访问域中,则按名称呼叫这些成员是非法的.仅仅因为你有一个成员并不意味着你可以使用它!

我们现在继续原来的答案:


CLR如何处理这个问题?

非常好.:-)

使值类型成为值类型的原因是它的实例是按值复制的.使引用类型成为引用类型的原因是它的实例是通过引用复制的.您似乎有一些信念认为值类型和引用类型之间的继承关系在某种程度上是特殊和不寻常的,但我不明白这种信念是什么.继承与事物的复制方式无关.

以这种方式看待它.假设我告诉你以下事实:

  • 有两种盒子,红盒子和蓝盒子.

  • 每个红色框都是空的.

  • 有三个特殊的蓝色方框,分别叫做O,V和E.

  • O不在任何盒子里面.

  • V在O里面.

  • E在V里面

  • V里面没有其他蓝盒子.

  • E内没有蓝框.

  • 每个红色框都在V或E中.

  • 除O之外的每个蓝色框本身都在蓝色框内.

蓝色框是引用类型,红色框是值类型,O是System.Object,V是System.ValueType,E是System.Enum,"内部"关系是"派生自".

这是一套完全一致且简单的规则,如果您有大量的纸板和耐心,您可以轻松实现这些规则.盒子是红色还是蓝色与内部无关; 在现实世界中,完全可以在蓝色框内放置一个红色框.在CLR中,创建一个继承自引用类型的值类型是完全合法的,只要它是System.ValueType或System.Enum即可.

那么让我们改一下你的问题:

ValueTypes如何从Object(ReferenceType)派生而仍然是ValueTypes?

每个红色框(值类型)是如何在内部(派生自)框O(System.Object),这是一个蓝色框(参考类型),仍然是一个红色框(值类型)?

当你这样说时,我希望这很明显.没有什么可以阻止你在盒子V里放一个红色盒子,盒子里面是蓝色的盒子.为什么会这样?


另外一个更新:

琼原来的问题是关于它如何可能是值类型从引用类型派生.我的原始答案并没有真正解释CLR使用的任何机制来解释我们在两个具有完全不同表示的事物之间存在推导关系的事实 - 即,所引用的数据是否具有对象头,同步块,它是否拥有自己的存储空间用于垃圾收集等等.这些机制很复杂,太复杂,无法在一个答案中解释.CLR类型系统的规则比我们在C#中看到的稍微简化的风格要复杂得多,例如,在类型的盒装版本和非盒装版本之间没有明显的区别.泛型的引入也导致了大量额外的复杂性被添加到CLR中.有关详细信息,请参阅CLI规范,特别注意装箱和受限虚拟调用的规则.

  • 语言结构应该是有意义的.具有从任意引用类型派生的任意值类型是什么意思*?您是否可以通过这样的方案完成任何事情,而这些方案也无法通过用户定义的隐式转换实现? (8认同)
  • 啊,我明白了.您希望使用继承而不是建模"是一种"关系的机制,而只是作为在一堆相关类型之间共享代码的机制.这似乎是一个合理的场景,尽管我个人试图避免使用继承纯粹作为代码共享的便利. (8认同)
  • Joan一旦定义了行为,就可以创建一个接口,让你想要共享的结构行为实现接口然后创建一个在接口上运行的扩展方法.这种方法的一个潜在问题是,在调用接口方法时,struct将首先被加框,并且复制的盒装值将被传递给扩展方法.状态的任何更改都将发生在对象的副本上,这可能对API的用户不直观. (3认同)
  • 我猜不会。我只是以为您可以将某些成员提供给您视为一个组的许多值类型,您可以使用抽象类来派生该结构。我想您可以使用隐式转换,但是您会为此付出性能损失,对吗?如果您正在做数百万个。 (2认同)
  • @Sipo:现在,公平地说,问题包括“ CLR如何处理此问题?” 答案很好地描述了CLR如何执行这些规则。但这就是问题:我们应该*期望*实现一种语言的系统没有与该语言相同的规则!实现系统必定是较低级的,但不要让该较低级系统的规则与基于其构建的较高级系统的规则相混淆。当然,正如我在回答中所指出的那样,CLR类型系统可以区分装箱和非装箱的值类型。**但是C#不会**。 (2认同)

Jar*_*Par 21

小修正,C#不允许结构来自任何东西,而不仅仅是类.所有结构都可以实现一个与派生非常不同的接口.

我认为回答这个问题的最佳方法ValueType是特别的.它本质上是CLR类型系统中所有值类型的基类.很难知道如何回答"CLR如何处理这个问题",因为它只是CLR的一个规则.

  • 你说`ValueType`是特殊的,但值得一提的是`ValueType`本身实际上是一个引用类型. (2认同)
  • @JoanVenge我相信这里的困惑是说结构是从CLR中的ValueType类派生的。我认为,在CLR中并不真正存在结构是更正确的说法,CLR中“ struct”的实现实际上是ValueType类。因此,这并不像struct是从CLR中的ValueType继承的。 (2认同)

Ree*_*sey 20

这是由CLR维护的一种有点人为的构造,以便允许将所有类型视为System.Object.

值类型派生自System.Object到System.ValueType,这是特殊处理发生的地方(即:CLR处理从ValueType派生的任何类型的装箱/取消装箱等).


yi.*_*han 6

你的陈述是不正确的,因此你的困惑.C#允许结构派生自类.所有结构都派生自同一个类System.ValueType

所以让我们试试这个:

 struct MyStruct :  System.ValueType
 {
 }
Run Code Online (Sandbox Code Playgroud)

这甚至都不会编译.编译器会提醒您"接口列表中的类型'System.ValueType'不是接口".

当反编译作为结构的Int32时,你会发现:

public struct Int32:IComparable,IFormattable,IConvertible {},不提及它派生自System.ValueType.但是在对象浏览器中,您确实发现Int32继承自System.ValueType.

所以这些让我相信:

我认为回答这个的最好方法是ValueType很特别.它本质上是CLR类型系统中所有值类型的基类.很难知道如何回答"CLR如何处理这个问题",因为它只是CLR的一个规则.


sup*_*cat 5

装箱值类型实际上是一种引用类型(它像一个人一样行走,像一个人一样嘎嘎叫,所以实际上它是一个)。我建议 ValueType 并不是真正的值类型的基本类型,而是基本引用类型,当转换为 Object 类型时,值类型可以转换为该基本引用类型。非装箱值类型本身位于对象层次结构之外。