继承基类时,C#默认为new而不是override有什么原因吗?

继承基类时,C#默认为new而不是override有什么原因吗?,c#,inheritance,C#,Inheritance,我知道覆盖和新行为之间的区别(或者相信无论如何都是这样),有几个问题描述了这两者之间的区别,但我的问题是,C#默认为新行为(带有警告)而不是默认为覆盖是否有特殊原因 public class Base { virtual public string GetString() => "Hello from Base"; } public class Child : Base { public string GetString() => "Hello from Child

我知道覆盖和新行为之间的区别(或者相信无论如何都是这样),有几个问题描述了这两者之间的区别,但我的问题是,C#默认为新行为(带有警告)而不是默认为覆盖是否有特殊原因

public class Base
{
    virtual public string GetString() => "Hello from Base";
}

public class Child : Base
{
    public string GetString() => "Hello from Child";
}

...

var childAsBase = (Base)new Child();
Console.WriteLine(childAsBase.GetString());

...

c:\>dotnet run
child.cs(5,23): warning CS0114: 'Child.GetString()' hides inherited member 'Base.GetString()'.
To make the current member override that implementation, add the override keyword. 
Otherwise add the new keyword. [C:\IPutAllMyProjectsInMyRootFolder.csproj]
Hello from Base
我可以认为,无论继承的方法是否标记为virtual,都可以获得相同的行为,但同时,声明它为virtual就是说“override me”,因此默认重写对我来说似乎是合理的

我脑海中闪过的另一个原因是使用虚拟函数表的成本,但这似乎是一个可怕的原因,因为作为一名程序员,我希望代码做的事情应该比节省cpu周期更重要。但也许在语言发明的时候,情况就不是这样了?

当涉及类型层次结构的C语言设计决策对您来说似乎不寻常时,一个好方法是问自己这样一个问题:“如果有人不告诉我就更改了我的基类,会发生什么?”C#是精心设计的,旨在降低脆弱基类故障的成本,这就是其中之一

让我们先考虑一个阴影方法有<代码>覆盖> <代码>关键字。< /p> 这向编译器表明派生类作者和基类作者正在合作。基类作者制作了一个可重写的方法,这是一件非常危险的事情。可重写方法意味着您不能编写测试该方法所有可能行为的测试用例!方法的可重写性必须在中设计,所以您必须说方法是虚拟的(或抽象的)

如果我们看到一个
重写
修饰符,那么我们就知道基类和派生类的作者都对这个危险的扩展点的正确性和安全性负责,并且已经成功地相互沟通以达成协议

让我们考虑阴影方法有<代码>新< /COD>关键字的情况。同样,我们知道派生类作者已经检查了基类,并且确定了隐藏方法(无论是否为虚拟方法)不满足派生类使用者的需求,并且故意做出了危险的决定,使用具有相同签名的两个方法

这就给我们留下了阴影方法既没有
覆盖
也没有
新建
的情况我们没有证据表明派生类的作者知道基类中的方法。事实上,我们有相反的证据;如果他们知道一个虚拟基类方法,他们会重写它以匹配虚拟方法的契约,如果他们知道一个非虚拟基类方法,那么他们会故意做出危险的决定来隐藏它

这种情况怎么会出现呢?我只想到两种方法

首先,派生类作者没有充分研究它们的基类,并且不知道它们刚刚隐藏的方法的存在,这是一个可怕的处境。派生类继承基类的行为,并可用于需要维护基类不变量的场景中!我们必须警告无知的开发人员,他们正在做一些极其危险的事情

其次,对基类进行更改后,将重新编译派生类。现在,派生类作者并不是不知道基类,因为它是最初编写的,并且是在他们设计派生类和测试派生类时。但他们不知道底层阶级已经改变的事实

同样,我们必须警告无知的开发人员,发生了一些他们需要做出重要决定的事情:如果可能的话重写,或者确认隐藏,或者重命名或删除派生类方法

这就证明了当阴影方法既没有标记为
new
也没有标记为
override
时,为什么必须给出警告。但那不是你的问题。您的问题是“为什么默认为
new
?”

好吧,假设您是编译器开发人员。当编译器面临缺少
new
override
的阴影方法时,以下是您的选择:

  • 什么也不做;不要给出警告或错误,选择一种行为。如果代码由于脆弱的基类失败而中断,那就太糟糕了。您应该更仔细地查看基类。显然,我们可以做得更好

  • 让它成为一个错误。现在,基类作者可以通过更改基类的成员来破坏您的构建。这不是一个糟糕的想法,但我们现在必须权衡期望的构建中断的成本——因为他们发现了一个bug——与不需要的构建中断的成本——在需要默认行为的情况下——与意外忽略警告和引入bug的成本

这是一个棘手的问题,各方都有争论。提出警告是一种合理的折衷立场;您可以随时打开“警告即错误”,我建议您这样做

  • 将其设置为警告,如果基方法可重写,则将其设置为重写;如果基方法不可重写,则将其设置为阴影。这不仅是不一致的,而且我们刚刚引入了另一种脆弱的基类失败。你看到了吗?如果基类作者将其方法从非虚拟更改为虚拟,或者反之亦然,该怎么办?这将导致意外地将阴影方法从覆盖更改为阴影,反之亦然
但让我们暂时把它放在一边。如果可能,自动覆盖的其他后果是什么?记住,场景的前提是重写是偶然的,派生类autho