为什么这个C#类声明编译?

时间:2009-07-21 02:14:38

标签: c# .net sealed

这个问题确实有点无意义,但我只是好奇:

此:

public sealed class MyClass
{
   protected void MyMethod(){}
}

编译,但发出警告

虽然这个:

public sealed class MyClass
{
   public virtual void MyMethod(){}
}

无法编译。出于纯粹的好奇心,这有什么理由吗?

9 个答案:

答案 0 :(得分:12)

virtual用于声明方法/属性“override-able”。

sealed用于声明该类不能继承。

因此,永远不能覆盖密封类中的虚方法,因为该类永远不会被继承。它没有意义。

protected影响对成员的访问,它并不像虚拟那样声明它“可覆盖”(尽管它经常以这种方式使用),因此并不矛盾。

答案 1 :(得分:4)

我看不出有充分理由这样做。受保护的MyMethod可以从MyClass中调用,但永远不会从派生类中调用(因为MyClass是密封的)。也允许直接从MyClass调用虚拟版本,但是该方法具有覆盖是非法的,因为您无法从MyClass派生类...

答案 2 :(得分:4)

我能想到的唯一原因是,有时您需要编写受保护的方法来覆盖其他受保护的方法。语言可以设计为允许这样:

protected override void Foo()

但不是这个

protected void Foo()

但这可能被认为有点难以理解 - 这是override缺席,这使得它无用,而在

的情况下
public virtual void Foo()

virtual存在是无用的。存在“错误”的东西可能比没有有用的东西更容易理解。

在这种情况下,虚拟化也可能具有性能影响,而制作保护而不是私有的东西可能不会 - 所以它会更严重。

这些只是猜测真的 - 如果我们真的很幸运,Eric Lippert会给出更明确的答案。他是你想要的人,而不是我:)

最佳答案:将警告视为错误,无论如何它们都是等效的;)

答案 3 :(得分:2)

密封类可以通过继承来保护成员。 当一个方法是一个类的一部分时,该方法是如何实现的并不重要。

在第一种情况下,对于密封类的protected方法,它与密封类继承受保护方法的方法相同。所以它编译。<​​/ p>

出于好奇,警告到底是什么?

答案 4 :(得分:2)

错误是:

  

CS0549:'function'是密封类'class'中的新虚拟成员。

首先,尽管在protected类中包含新的virtualsealed成员实际上没有意义,但CLI¹确实允许它。 CLI还允许使用callvirt IL指令调用密封类的成员,即使编译器可以使用call指令自由替换它。

目前,我在ECMA-334(C#语言规范)中找不到任何需要编译器发出上述错误的内容。看起来像微软的实现添加了错误只是因为在密封的类中包含新的虚拟成员是没有意义的。

¹CLI是一个虚拟机,C#编译器会发出在其上运行的字节代码。由于这个原因,几乎所有在CLI中非法的概念在C#中都是非法的 - 但这是C#做多一点的情况(不是说它有问题)。

编辑:似乎发布标记的帖子正在解释为什么没有意义在OP中编写类似的代码。但是关于什么规则使它成为编译器错误,它们似乎是错误的。

答案 5 :(得分:1)

密封类不能分类,因此虚拟不是一种选择。因此错误。

这首先是有点愚蠢但有效,因此警告。

答案 6 :(得分:0)

我猜测编译器会使用密封类进行一些优化,如果你声明了一个虚拟方法是不可能的 - “没有vtable”似乎是一个可能的候选者。

但这只是猜测。

答案 7 :(得分:0)

密封When applied to a class, the sealed modifier prevents other classes from inheriting from it.

这里我试图逐一解释你:

public sealed class MyClass
{
   protected void MyMethod(){}
}

它会给你警告,因为实际上它没有任何意义,因为在声明一个密封的类之后你不能继承它,因为你的方法是protected所以你不能使用它的对象在类外访问它(并且还要记住,你不能创建一个这样的子类,所以你也不能通过这个技巧使用这个方法。)实际上,使它成为protected是没有意义的,所以编译器会给你一个警告但是如果你把它设为publicinternal那么它就不会给你错误,因为它在这种情况下很有用。

现在是第二个:

public sealed class MyClass
{
   public virtual void MyMethod(){}
}

当你密封你的课程,现在你正在使你的方法成为虚拟的,所以你间接地向某人提供覆盖它的提议,这只能通过继承来实现。这就是问题所在。你上课是密封的,所以你无法使用此类执行继承。因此,virtual会导致错误。

我希望它能帮助你理解。

供参考http://msdn.microsoft.com/en-us/library/88c54tsw.aspx

答案 8 :(得分:0)

声明一个新的受保护成员意味着有意与后代类共享该成员。密封的类不能有后代,因此声明一个新的受保护成员有点矛盾,就像在密封类中声明一个新的虚拟方法一样。

至于为什么虚拟产生错误而受保护只产生警告,我只能推测它可能与新虚拟方法需要编译器为类型(vtable)构建数据结构的事实有关,而新的受保护成员只有一个访问标志集 - 没有新的数据结构。如果禁止编译器为密封类创建vtable,如果它遇到新的虚方法应该怎么办?编译失败。密封类中新的受保护方法毫无意义,但并不要求编译器冒险进入禁区。