为什么.NET值types是密封的?
从C#结构中inheritance是不可能的。 这不是很明显,为什么这是:
- 显然你不能有一个从值typesinheritance的引用types; 这是行不通的
- 从原始types(Int32,Double,Char等)中inheritance它是不合理的。
- 你需要能够使用派生实例调用基类(非虚函数)。 你可以从派生的结构体转换到基体,因为它们会重叠相同的内存。 我猜从基础铸造派生将无法正常工作,因为你不知道在运行时派生的结构types。
- 我可以看到你不能在你的类层次结构中实现虚拟方法,因为值types不能有虚拟成员
我不知道这是CLR的技术限制,还是C#编译器阻止你做的事情?
编辑:值types不能有虚拟方法,我意识到这种限制排除了大多数情况下,你想使用inheritance。 尽pipe如此,这仍然留下了inheritance。 想象一下带有Colour
字段的Shape
结构体:我可以编写接受任何从Shape
派生的结构体的代码,并访问它的Colour
字段,即使我永远不会写一个虚拟的Shape.Draw
方法。
我可以想象一个会被非密封值types破坏的场景。 值types应该正确实现Equals
和GetHashCode
; 即使System.Object
上的这两个方法是虚拟的,它们也会在值types上被非虚拟调用。 即使值types不封闭,写一个从另一个派生的结构的人也不能编写他们自己的这两个方法的实现,并希望正确地调用它们。
我应该指出,我并不是说我应该能够从自己的代码中inheritance结构。 但是我想要做的是猜测为什么这个特定的代码被.NET禁止。
编辑2:我刚刚发现了这个非常类似的问题 ,答案是有效的,因为那么价值types的数组将不起作用。
原因是大多数inheritance技术与运行时多态(虚拟函数)有关,并且这些技术不适用于值types:对于运行时多态具有任何意义,对象需要被视为引用 – 这不是特定于.NET的,这只是虚拟function如何实现的技术细节。
值types形成了.NET规则的一个例外,正是为了允许轻量级对象不需要间接引用。 所以运行时多态性不适用于他们,inheritance的大部分方面变得毫无意义。
(有一个例外:一个值types的对象可以被装箱,这允许调用从System.Object
inheritance的虚拟方法。)
为了解决您的一个观点:
- 你可以从派生的结构体转换到基体,因为它们会重叠相同的内存。
不,这是不可能的 – 铸造一个值types会复制它的值。 我们不在这里处理引用,所以在内存中没有重叠。 因此将一个值types转换为其基本types是没有意义的(同样,除非我们正在讨论转换为实际执行装箱的object
,并且还对值的副本进行操作)。
还不清楚? 我们来看一个例子。
比方说,我们已经得到了假设的struct Shape
并且从中inheritance了struct Circle
。 Shape
定义了一个虚拟的Draw
方法(它接受一个Graphics
对象)。 现在,假设我们要在canvas上绘制一个形状。 这当然是非常好的:
var circle = new Circle(new Point(10, 10), 20); circle.Draw(e.Graphics); // e.Graphics = graphics object of our form.
但是在这里我们实际上并没有使用inheritance。 为了使用inheritance,请设想一下下面的DrawObject
辅助方法:
void DrawObject(Shape shape, Graphics g) { // Do some preparation on g. shape.Draw(g); }
我们用Circle
称呼它:
var circle = new Circle(new Point(10, 10), 20); DrawObject(circle, e.Graphics);
– 而且, ka-blam – 这个代码并没有画出一个圆圈。 为什么? 因为当我们将圆圈传递给DrawObject
方法时,我们做了两件事:
- 我们复制它。
- 我们切片 ,即对象
shape
对象实际上不再是一个Circle
– 既不是原来的,也不是一个副本。 相反,它的Circle
部分在复制过程中被“切掉”,只剩下Shape
部分。shape.Draw
现在调用Shape
的Draw
方法,而不是Circle
。
在C ++中,实际上可能导致此行为。 因此,C ++中的OOP只能用于指针和引用,而不能直接用于值types。 出于同样的原因,.NET只允许引用types的inheritance,因为无论如何您都不能将它用于值types。
注意,如果Shape
是一个接口,上面的代码可以在.NET 中工作。 换句话说,一个引用types。 现在的情况是不同的:你的circle
对象仍然会被复制,但是它也会被装入一个引用。
现在,.NET 在理论上可以让你inheritance一个class
的struct
。 那么上面的代码就可以工作,如果Shape
是一个接口。 但是,首先有一个struct
的整体优势消失了:对于所有的意图和目的(除了局部variables永远不会传递给另一个方法,因此没有inheritance的效用),你的struct
将会像一个不可变的引用types值types。
从ECMA 335:价值types将被密封,以避免处理值切片的复杂性。 这里指定的更严格的规则允许更有效的实现,而不会严重影响function。
我不知道“价值切割”是什么意思,但我猜想它们是封闭的,以便有效实施CLR。
您可以使用值types上的genericstypes参数进行一些有限的inheritance。
因为value-type的每个实例都具有不同的大小并存储在堆栈中。 所以,如果你写“Base = Derived”,其中“Base”和“Derived”是值types,你将会损坏堆栈。