这个问题确实有点毫无意义,但我只是好奇:
这:
public sealed class MyClass
{
protected void MyMethod(){}
}
编译,但给出警告
而这:
public sealed class MyClass
{
public virtual void MyMethod(){}
}
不编译。纯粹出于好奇,这是有原因的吗?
我看不出有什么好的理由。可以从 MyClass 调用受保护的 MyMethod,但永远不会从派生类调用(因为 MyClass 是密封的)。虚拟版本也可以直接从MyClass调用,但是方法有override是非法的,因为不能从MyClass派生类...
我能想到的唯一原因是,有时您需要编写受保护的方法来覆盖其他受保护的方法。该语言可以设计为允许这样做:
protected override void Foo()
但不是这个
protected void Foo()
但这可能看起来有点难以理解 - 正是缺少这使得override
它毫无用处,而在
public virtual void Foo()
它的存在是virtual
无用的。某些“错误”的存在可能比没有有用的东西更容易理解。
在这种情况下,虚拟化也可能会对性能产生影响,而将某些东西设为受保护而不是私有可能不会——所以它更严格一些。
这些只是猜测——如果我们真的很幸运,Eric Lippert 会给出一个更明确的答案。他是你想要的人,不是我:)
最佳答案:将警告视为错误,无论如何它们都是等价的;)
密封类可以通过继承具有受保护的成员。当方法是类的一部分时,该方法如何到达那里并不重要。
在第一种情况下,使用密封类上的受保护方法,就好像密封类继承了受保护方法一样。所以它编译。
出于好奇,给出的警告到底是什么?
错误是:
CS0549:“函数”是密封类“类”中的新虚拟成员。
首先,尽管在一个类中包含新成员protected
或成员实际上并没有什么意义,但 CLI¹ 确实允许这样做。CLI 还允许使用 IL 指令调用密封类的成员,即使编译器可以自由地将其替换为指令。virtual
sealed
callvirt
call
目前,我在 ECMA-334(C# 语言规范)中找不到任何需要编译器发出上述错误的内容。看起来微软的实现添加了错误只是因为在密封类中包含新的虚拟成员没有意义。
¹CLI 是一个虚拟机,C# 编译器会发出在其上运行的字节码。由于这个原因,几乎所有在 CLI 中非法的概念在 C# 中也是非法的——但这是 C# 做了一些额外的事情(并不是说这是个问题)。
编辑:似乎被标记的帖子正在解释为什么在 OP 中编写这样的代码没有意义。但是关于什么规则使它成为编译器错误,他们似乎是错误的。
密封类不能被子类化,因此 virtual 不是一个选项。因此错误。
这首先有点愚蠢但有效,因此发出警告。
我猜编译器对密封类做了一些优化,如果你声明了一个虚拟方法,这是不可能的——“没有 vtable”似乎是一个可能的候选者。
不过,这只是一个猜测。
密封的When applied to a class, the sealed modifier prevents other classes from inheriting from it.
在这里,我试图为您一一解释:
public sealed class MyClass
{
protected void MyMethod(){}
}
它给你警告,因为实际上它没有任何意义,因为在将一个类声明为密封后你不能继承它,并且你的方法是protected
这样你不能使用它的对象在类之外访问它(并且还要记住你可以'不要创建这个的子类,所以你也不能通过那个技巧来使用这个方法)。所以实际上制作它是没有意义的,protected
所以编译器会给你一个警告,但如果你这样做,public
或者internal
然后它不会给你错误,因为它在这种情况下很有用。
现在是第二个:
public sealed class MyClass
{
public virtual void MyMethod(){}
}
当你密封你的类时,现在你将你的方法设置为虚拟的,所以间接地你正在向某人提供覆盖它的提议,而这只能通过继承来实现,问题来了。你的类是密封的,所以你不能用这个类执行继承。所以这就是virtual
它给出错误的原因。
我希望它能帮助你理解。
声明一个新的受保护成员意味着与后代类共享该成员的意图。密封类不能有后代,因此声明一个新的受保护成员有点矛盾,就像在密封类中声明一个新的虚方法一样。
至于为什么 virtual 产生错误而 protected 只产生警告,我只能推测这可能与新的虚拟方法需要编译器为类型(vtable)构建数据结构,而新的受保护成员只设置了访问标志 - 没有新的数据结构。如果禁止编译器为密封类创建vtable,遇到新的虚方法怎么办?编译失败。密封类中的新受保护方法毫无意义,但不需要编译器冒险进入禁区。