为什么 .NET 值类型是密封的? [英] Why are .NET value types sealed?

查看:21
本文介绍了为什么 .NET 值类型是密封的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

不可能从 C# 结构继承.我不清楚这是为什么:

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

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

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

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

值类型不能有虚方法,我意识到这个限制排除了大多数你想使用继承的场景.不过,这仍然留下继承作为聚合.想象一个带有 Colour 字段的 Shape 结构:我可以编写接受从 Shape 派生的任何结构的代码,并访问它的 Colour 字段,即使我永远无法编写虚拟的 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.

我可以想到一种会被非密封值类型破坏的场景.值类型应该正确实现 EqualsGetHashCode;尽管 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.

我应该指出,我并不是建议我应该能够在我自己的代码中从结构继承.不过,我想要做的是猜测为什么 .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:我刚刚发现了这个非常相似的问题a>,答案实际上是因为值类型的数组将不起作用".

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 规则的一个例外,正是为了允许不需要通过引用间接访问的轻量级对象.所以运行时多态对它们不起作用,继承的大部分方面都变得毫无意义.

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.)

解决您的一个观点:

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

不,这是不可能的——转换一个值类型会复制它的值.我们在这里不处理引用,所以内存中没有重叠.因此,将值类型转换为其基类型是没有意义的(同样,除非我们谈论的是转换为 object,它实际上在幕后执行装箱,并且 复制值).

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).

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

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

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 辅助方法:

– 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);
}

我们在别处用 Circle 调用它:

And we call it elsewhere with a Circle:

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

– 而且,ka-blam – 此代码不绘制圆圈.为什么?因为当我们将圆传递给 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对象实际上不再是Circle——既不是原始的也不是副本.取而代之的是,它的 Circle 部分在复制过程中被切片"了,只剩下 Shape 部分.shape.Draw 现在调用 ShapeDraw 方法,而不是 Circle.
  • 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++ 中的 OOP 仅适用于指针和引用,而不是直接适用于值类型.出于同样的原因,.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.

请注意,如果 Shape 是一个接口,上面的代码确实可以在 .NET 中工作.换句话说,一个 reference 类型.现在情况不同了:您的 circle 对象将仍然被复制,但它也会被装箱到一个引用中.

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 可以理论上允许您从 class 继承 struct.那么上面的代码就像 Shape 是一个接口一样工作.但是,首先拥有 struct 的全部优势就消失了:对于所有意图和目的(除了从不传递给另一个方法的局部变量,因此没有继承的实用程序)您的 struct 将表现为不可变的引用类型,而不是值类型.

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.

这篇关于为什么 .NET 值类型是密封的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆