代码之家  ›  专栏  ›  技术社区  ›  Tim Robinson

为什么.NET值类型是密封的?

  •  38
  • Tim Robinson  · 技术社区  · 16 年前

    从C#struct继承是不可能的。我不清楚为什么会这样:

    • 您需要能够使用派生实例在基础上调用(非虚拟)方法。您可以从派生结构强制转换到基结构,因为它们会重叠相同的内存。我猜从基到派生的强制转换不起作用,因为在运行时您无法知道派生结构的类型。
    • 我可以看出,您无法在类层次结构中实现虚拟方法,因为值类型不能有虚拟成员

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

    编辑: Shape Colour field:我可以编写接受从中派生的任何结构的代码 形状 颜色 字段,即使我永远也写不出虚拟 Shape.Draw 方法。

    Equals GetHashCode System.Object

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

    编辑2: 我刚刚发现 this very similar question

    4 回复  |  直到 9 年前
        1
  •  58
  •   Konrad Rudolph    16 年前

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

    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.
    

    但在这里,我们实际上根本不使用继承。要使用继承,请想象以下内容 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);
    

    以及, 卡巴姆 这个代码没有画圆。为什么?因为当我们把圆圈传给

    • 我们 信息技术
    • shape 对象真的不再是一个 圆圈 既不是原件也不是副本。相反,它是 圆圈 部分在复制过程中被切掉,只有 剩下的一部分。 shape.Draw 现在是 方法 ,不属于 圆圈 .

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

    请注意,上面的代码 如果需要,请在.NET中工作 形状 是一个接口。换句话说,一个 参考 类型。现在情况不同了:你的 circle 仍然 将被复制,但也将被装箱到引用中。

    现在,.NET 能够 理论上允许你继承 struct class . 那么上面的代码就可以像 形状 我们是一个接口。但是,拥有一个 首先,消失:为了所有目的和目的(除了 从未 结构

        2
  •  10
  •   logicnp    16 年前

    来自ECMA 335: 值类型将被密封,以避免处理值切片的复杂性。 损害功能。

    我不知道“值切片”是什么意思,但我猜它们是密封的,以便高效地实现CLR。

        3
  •  1
  •   leppie    16 年前

    您可以在值类型上使用泛型类型参数进行某种有限形式的继承。

        4
  •  -2
  •   user215303    16 年前

    因为值类型的每个实例都有不同的大小并存储在堆栈上。所以,如果您编写“Base=Derived”,其中“Base”和“Derived”是值类型,则会损坏堆栈。