为什么.NET值类型被密封?

Tim*_*son 38 .net c# struct

从C#结构继承是不可能的.这对我来说并不明白为什么会这样:

  • 显然,您不能拥有从值类型继承的引用类型; 这不行
  • 从一个原始类型(Int32,Double,Char等)继承它听起来是不合理的.
  • 您需要能够使用派生实例在基础上调用(非虚拟)方法.您可以从派生结构转换为基础,因为它们将重叠相同的内存.我想从基础到派生的转换是行不通的,因为你无法在运行时知道派生结构的类型.
  • 我可以看到您无法在类层次结构中实现虚方法,因为值类型不能包含虚拟成员

我想知道这是否是CLR中的技术限制,还是C#编译器阻止你做的事情?

编辑:值类型不能有虚拟方法,我意识到这个限制排除了大多数你想要使用继承的场景.但是,这仍然会留下继承 - 聚合.想象一个Shape带有Colour字段的结构:我可以编写接受任何结构派生的代码Shape,并访问其Colour字段,即使我永远不能编写虚Shape.Draw方法.

我可以想到一个会被非密封值类型破坏的场景.值类型都应该落实EqualsGetHashCode正确; 即使这两个方法System.Object都是虚拟的,它们也会在值类型上被非虚拟地称为.即使值类型没有被密封,编写从另一个派生的结构的人也无法编写自己的这两种方法的实现,并期望正确调用它们.

我应该指出,我并不是说我应该能够从我自己的代码中继承结构.但是,我想要做的是猜测为什么.NET禁止这种特殊代码气味.

编辑2:我刚刚发现了这个非常相似的问题,其答案实际上是"因为那时价值类型的数组不起作用".

Kon*_*lph 52

原因是大多数继承技术与运行时多态(虚函数)有关,而那些对值类型无效:对于运行时多态有任何意义,对象需要被视为引用 - 这不是特定于.NET的,它只是虚拟功能如何实现的技术细节.

值类型构成了.NET规则的一个例外,正是为了允许轻量级对象不需要通过引用进行间接寻址.因此,运行时多态性对它们不起作用,并且继承的大多数方面都变得毫无意义.

(有一个例外:可以装箱值类型对象,允许System.Object调用从中继承的虚方法.)

要解决您的一个要点:

  • 您可以从派生结构转换为基础,因为它们将重叠相同的内存.

不,这是不可能的 - 投射值类型会复制其值.我们这里没有处理引用,因此内存中没有重叠.因此,将值类型转换为其基类型是没有意义的(再次,除非我们讨论转换到object实际执行装箱的装箱,并且操作值的副本).

还不清楚吗?我们来看一个例子.

假设我们已经得到了假设,struct Shape并从中继承了struct Circle.Shape定义一个虚拟Draw方法(接受一个Graphics对象).现在,假设我们想在画布上绘制一个形状.当然,这非常有效:

var circle = new Circle(new Point(10, 10), 20);
circle.Draw(e.Graphics); // e.Graphics = graphics object of our form.
Run Code Online (Sandbox Code Playgroud)

- 但是在这里我们根本没有使用继承.要使用继承,请设想以下DrawObject辅助方法:

void DrawObject(Shape shape, Graphics g) {
    // Do some preparation on g.
    shape.Draw(g);
}
Run Code Online (Sandbox Code Playgroud)

我们在其他地方称之为Circle:

var circle = new Circle(new Point(10, 10), 20);
DrawObject(circle, e.Graphics);
Run Code Online (Sandbox Code Playgroud)

- 而且,ka-blam - 这段代码没有绘制圆圈.为什么?因为当我们将圆圈传递给DrawObject方法时,我们会做两件事:

  • 我们复制它.
  • 我们对它进行切片,即对象shape对象实际上不再是Circle- 既不是原始对象也不是副本.相反,它的Circle部分在复制过程中被"切掉",只剩下Shape部分.shape.Draw现在调用的Draw方法Shape,而不是Circle.

在C++中,您实际上可以导致此行为.因此,C++中的OOP只能用于指针和引用,而不能直接用于值类型.出于同样的原因,.NET只允许继承引用类型,因为无论如何都不能将它用于值类型.

请注意,如果是接口,上面的代码在.NET中可以正常工作Shape.换句话说,是引用类型.现在情况有所不同:您的circle对象仍将被复制,但它也将被装入参考.

现在,.NET 可以在理论上让你继承structclass.然后上面的代码就像Shape接口一样工作.但是,struct首先拥有a的整个优势消失了:对于所有意图和目的(除了永远不会传递给另一个方法的局部变量,因此没有继承的实用程序),你的struct行为将是一个不可变的引用类型而不是一个值类型.


log*_*cnp 9

从ECMA 335:价值类型将被密封,以避免处理价值切片的复杂性.此处指定的限制性规则允许更有效的实施,而不会严重损害功能.

我不知道'值切片'是什么意思,但我猜它们是密封的,以便有效地实现CLR.