为什么.NET值类型封了?封了、类型、NET

2023-09-02 21:09:57 作者:只是歌词太伤心

这是不可能从一个C#结构继承。这不是明显对我来说这是为什么:

It's not possible to inherit from a C# struct. It's not obvious to me why this is:

显然,你不能有继承值类型引用类型;这将无法正常工作 这听起来并不合理地从一个继承原始类型(的Int32,双人间,字符等) 您就需要能够调用(非虚拟)使用派生的实例的基础上的方法。可以投从派生结构的基础上,因为它们将重叠相同的存储器。我猜从基地派生是行不通的,因为你不可能不知道派生结构在运行时的类型转换。 在我看得出来你无法实现你的类层次结构中的虚拟方法,因为值类型不能有虚成员

我不知道这是在CLR的技术限制,或者一些C#编译器阻止你在干什么?

I wonder if this is a technical limitation in the CLR, or something that the C# compiler stops you from doing?

编辑:值类型不能有虚方法,我意识到这个限制排除了大多数情况下,您会希望使用继承。这仍然留下遗产,为聚集,虽然。想象一下,一个图形结构与颜色字段:我可以写code,它接受来自图形,并访问其颜色字段,即使我不能写一个虚拟的 Shape.Draw 方法。

Value types can't have virtual methods, and I realise this limitation rules out most scenarios in which you'd want to use inheritance. That still leaves inheritance-as-aggregation, though. Imagine a Shape struct with a Colour field: I can write code that accepts any struct derived from Shape, and access its Colour field, even if I can never write a virtual Shape.Draw method.

我能想到一个场景,将获得由非密封值类型破碎。值类型都应该实施等于 GetHash code 正确;即使在 System.Object的这两种方法都是虚拟的,他们得到所谓的非无形中就值类型。即使值类型是不密封的,写的人从另一个衍生的结构不能写自己的实现这两种方法,并希望让他们正确调用。

I can think of one scenario that would get broken by non-sealed value types. Value types are supposed to implement Equals and GetHashCode correctly; even though these two methods on System.Object are virtual, they get called non-virtually on value types. Even if value types weren't sealed, someone writing a struct derived from another one couldn't write their own implementation of these two methods and expect to have them called correctly.

我要指出的是,我并不是说我应该能够从结构继承自己的code。我试图做的,虽然是猜测,为什么这个特殊的code气味是由.NET禁止的。

I should point out that I'm not suggesting I should be able to inherit from structs in my own code. What I am trying to do, though, is to guess why this particular code smell is forbidden by .NET.

编辑2:我只是看上此非常类似的问题,答案是有效的,因为这样的价值类型的数组是行不通的。

Edit 2: I just spotted this very similar question, the answer to which is effectively "because then arrays of value types wouldn't work".

推荐答案

的原因是,大多数遗传技术涉及到运行时多态性(虚拟函数)和那些没有价值类型的工作:为运行时多态性有任何意义,对象需要被当作参考 - 这是不是专门针对.NET或者,它只是功能如何虚拟实现技术细节

The reason is that most inheritance techniques relate to runtime polymorphism (virtual functions) and those don’t work on value types: for runtime polymorphism to have any meaning, objects need to be treated as references – this isn’t specific to .NET either, it’s simply a technical detail of how virtual functions are implemented.

值类型形成一个例外。NET的规则,precisely让轻的物体不通过引用需要间接。所以运行时多态性不为他们工作和继承的许多方面变得毫无意义。

Value types form an exception to .NET’s rule, precisely to allow lightweight objects that don’t require indirection via references. So runtime polymorphism doesn’t work for them and most aspects of inheritance become meaningless.

(这里有一个例外:数值类型的对象可以盒装,它允许从 System.Object的继承了被称为虚拟方法)

(There’s an exception: a value type object can be boxed, which allows for virtual methods inherited from System.Object to be called.)

要解决的要点之一:

  您可以投从派生结构的基础上,因为它们将重叠相同的存储器。   

没有,这是不可能的 - 铸件值类型会的复制的它的价值。我们不处理此引用,所以在内存中没有重叠。铸造一个值类型其基本类型,因此意义(同样,除非我们在谈论转化为对象这实际上引擎盖下进行拳击,和也的操作上的复制的值)。

No, this would not be possible – casting a value type would copy its value. We’re not dealing with references here, so no overlap in memory. Casting a value type to its base type is therefore meaningless (again, unless we’re talking about conversion to object which actually performs boxing under the hood, and also operates on a copy of the value).

还不清楚?让我们来看一个例子。

Still not clear? Let’s look at an example.

比方说,我们已经得到了假想结构形状,然后从它继承,在结构圆图形定义了一个虚拟的绘图方法(它接受一个图形目的)。现在,让我们说,我们要画的形状的画布上。这当然,工作得很好:

Let’s say we’ve got the hypothetical struct Shape and, inheriting from it, the struct Circle. Shape defines a virtual Draw method (which accepts a Graphics object). Now, let’s say we want to draw a shape on a canvas. This, of course, works perfectly well:

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

- 但在这里,我们实际上并不使用继承的。要使用继承,而不是想象以下 DrawObject helper方法:

– But here we don’t actually use inheritance at all. To make use of inheritance, imagine instead the following DrawObject helper method:

void DrawObject(Shape shape, Graphics g) {
    // Do some preparation on g.
    shape.Draw(g);
}

和我们称之为外地与

var circle = new Circle(new Point(10, 10), 20);
DrawObject(circle, e.Graphics);

- 和 KA-BLAM - 这code不画了一个圈。为什么?因为当我们通过圈内的 DrawObject 方法,我们做两件事情:

– And, ka-blam – this code doesn’t draw a circle. Why? Because when we pass the circle to the DrawObject method, we do two things:

我们的复制的吧。 我们的片的它,即对象形状对象实际上不再是 - 既不是原来的,也不是一个副本。相反,它的部分被切走在复制,只有图形部分保持。 shape.Draw 现在调用 图形的画的方法,没有的圆。 We copy it. We slice it, i.e. the object shape object is really no longer a Circle – neither the original one nor a copy. Instead, its Circle portion was "sliced" away during copying and only the Shape portion remains. shape.Draw now calls the Draw method of Shape, not of Circle.

在C ++中,你实际上可以导致此行为。因此,面向对象的C ++仅适用于指针和引用,而不是直接值类型。而对于同样的原因,.NET只允许引用类型的继承,因为你无法使用它的值类型反正。

In C++, you can actually cause this behaviour. For that reason, OOP in C++ only works on pointers and references, not on value types directly. And for that same reason, .NET only allows inheritance of reference types because you couldn’t use it for value types anyway.

请注意,上面的code的确实的工作,.NET,如果图形是一个接口。换句话说,一个的引用的类型。现在情况不同了:你的对象会的还是的被复制,但它也将被装箱为基准

Notice that the above code does work in .NET if Shape is an interface. In other words, a reference type. Now the situation is different: your circle object will still be copied but it will also be boxed into a reference.

现在,.NET的可以的理论让你继承结构。接着上面的code将工作一样好,如果图形是一个接口。但随后,其全优势结构在首位消失:为所有意图和目的(除了本地变量其中的永远的获得通过另一种方法,继承因此没有效用)的结构将表现为一个不可变的引用类型而不是值类型。

Now, .NET could theoretically allow you to inherit a struct from a class. Then the above code would work just as well as if Shape were an interface. But then, the whole advantage of having a struct in the first place vanishes: for all intents and purposes (except for local variables which never get passed to another method, hence no utility of inheritance) your struct would behave as an immutable reference type instead of a value type.