- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我知道重写和新方法之间的区别(或者仍然相信这样做),并且有几个问题描述了两者之间的区别,但是我的问题是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
最佳答案
当涉及类型层次结构的C#语言设计决策对您来说很不常见时,一种好技巧是问自己一个问题:“如果有人不告诉我就更改了我的基类,将会发生什么?” C#是经过精心设计的,目的是减轻脆性基类故障的成本,这就是其中之一。
我们首先考虑阴影方法具有override
关键字的情况。
这向编译器指示派生类作者和基类作者正在合作。基类作者创建了一个可重写的方法,这是一件非常危险的事情。可重写的方法意味着您无法编写测试该方法所有可能行为的测试用例!必须设计一种方法的可重写性,因此您必须说一种方法是虚拟的(或抽象的)。
如果我们看到一个override
修饰符,则说明基类和派生类作者都对该危险扩展点的正确性和安全性负责,并已成功地相互沟通以就合同达成协议。
接下来,让我们考虑阴影方法具有new
关键字的情况。再一次,我们知道派生类作者已经检查了基类,并确定带阴影的方法(无论是否是虚拟的)都不能满足派生类消费者的需求,并且故意做出了危险的决定以使用两种方法具有相同的签名。
这就给我们留下了阴影方法既没有override
也没有new
的情况。我们没有证据表明派生类的作者知道基类中的方法。实际上,我们有相反的证据。如果他们知道虚拟基类方法,则将其重写以匹配虚拟方法的约定,如果他们知道非虚拟基类方法,则将故意做出危险的决定以使其模糊。
这种情况怎么会出现?我想到的只有两种方法。
首先,派生类作者对他们的基类没有足够的研究,并且不知道他们刚刚使用的方法的存在,这是一个可怕的位置。派生类继承了基类的行为,可以使用在需要维护基类不变式的情况下!我们必须警告无知的开发人员,他们正在做极其危险的事情。
其次,在对基类进行更改之后,将重新编译派生类。现在,派生类的作者并不知道基类,因为它是原始编写的,在他们设计派生类时,以及在测试派生类时。但是他们不知道基类已经改变的事实。
同样,我们必须警告无知的开发人员发生了一些事情,他们需要做出重要的决定:尽可能覆盖,确认隐藏,重命名或删除派生的类方法。
这样就证明了为什么在阴影方法未标记为new
或override
时必须发出警告的理由。但这不是你的问题。您的问题是“为什么默认为new
?”
好吧,假设您是编译器开发人员。当编译器遇到缺少new
和override
的阴影方法时,可以选择以下方法:
没做什么;不要给出警告或错误,并选择一种行为。如果代码由于脆性基类故障而中断,那就太糟糕了。您应该更仔细地研究基类。显然,我们可以做得更好。
使其成为错误。现在,基类作者可以通过更改基类的成员来破坏您的构建。这不是一个糟糕的主意,但是我们现在必须权衡所需的构建中断的成本-因为他们发现了一个错误-与不需要的构建中断的成本-需要默认行为的情况-与忽略构建成本的开销意外警告并引入错误。
这是一个棘手的电话,各方都有争论。引入警告是一个合理的妥协立场;您可以始终打开“警告是错误”,我建议您这样做。
发出警告,如果基本方法可覆盖,则将其覆盖;如果基本方法不可覆盖,则将其覆盖。这不仅不一致,而且我们刚刚引入了另一种脆弱的基类故障。你看到了吗?如果基类作者将其方法从非虚拟方法更改为虚拟方法,反之亦然呢?这将导致意外遮蔽方法从覆盖更改为阴影,反之亦然。
但是,暂时暂时将其搁置一旁。如果可能,自动覆盖的其他后果是什么?请记住,该场景的前提是重写是偶然的,派生类的作者不了解基类的实现细节,不变式和公共表面积。
与仅更改通过派生类型的接收器调用阴影方法的那些调用者的行为的危险相比,自动更改基类方法的所有调用者的行为似乎具有疯狂的危险。
发出警告,默认为阴影而不是覆盖。通常,此选择更安全,它避免了第二种脆弱的基故障,避免了构建中断,具有基类接收器的方法的调用者获得了其测试用例所期望的行为,而具有派生类接收器的方法的调用者获得了测试用例所期望的行为。他们期望的行为。
所有设计选择都是仔细权衡许多互不兼容的设计目标的结果。 C#的设计人员特别关注大型团队,他们正在研究版本化的软件组件,在这些组件中,基类可能会以意想不到的方式进行更改,并且团队之间可能无法很好地交流这些更改。
我不介意的另一个原因是使用虚拟函数表的成本,但是就我作为编码人员的要求代码要比保存cpu周期更重要的意义而言,这似乎是一个令人恐惧的原因。但是也许是当语言被发明时,事实并非如此吗?
虚拟方法会带来成本;显而易见的代价是在运行时需要进行额外的表跳转,并需要获得所需的代码。还有一些不太明显的成本,例如:抖动无法内联对非密封方法的虚拟调用,等等。
但是,正如您所指出的,将非虚拟设置为默认值的原因并非主要是为了提高性能。主要原因是虚拟化非常危险,需要谨慎设计。必须记录和传达必须由覆盖方法的派生类维护的不变式。正确设计类型层次结构是昂贵的,并且选择加入可以降低成本并提高安全性。坦白说,我也希望默认也是密封的。
关于c# - 继承基类时,C#是否默认为new而不是override是有原因的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46793770/
有没有在OpenJDK 1.7.0_45中派生类重写基类方法,但没有@Override注解,运行时派生类实例调用基类方法的情况? class Base { public f() { }
我正在尝试在 C++ 中练习 OOP,但我遇到了有关覆盖函数的问题。在我的 Shape2D 和 Shape3D 类中,我有在 Square 和 Sphere 类(分别为 ShowArea() 和 Sh
我想控制值在槽中的保存方式以及读取槽时返回的内容。这是我的类定义: (defclass object () ((name :accessor name-access :initf
我正在尝试在 C++ 中练习 OOP,但我遇到了有关覆盖函数的问题。在我的 Shape2D 和 Shape3D 类中,我有在 Square 和 Sphere 类(分别为 ShowArea() 和 Sh
我读了section在 Scala 编程中,引入了抽象重写,但我仍然对这些修饰符的连接到底意味着什么感到困惑。使用这些修饰符的代码片段粘贴在下面: trait Doubling extends Int
阅读Javadoc对于 @Override 注释,我遇到了以下规则: If a method is annotated with thisannotation type compilers are r
我正在基于 BEP20Token 模板 (https://github.com/binance-chain/bsc-genesis-contract/blob/master/contracts/bep
关于下面提到的 3 份契约(Contract): 1) Whenever hashCode() is invoked on the same object more than once during
在 C# 中,override 默认启用,那么,是否不需要显式在基类中将方法声明为可覆盖?如果是的话 Overridable 仅限于 VB.NET 还是在 C# 中也是必需的? 因此可以覆盖哪些类型的
以下代码在 public void onClick 行生成此错误消息。 Multiple markers at this line - implements android.view.View.OnC
当我在运行 IIS 的服务器 2012R2 上托管它时,我能够使用 Autorest 和我的 api 生成代码 但是,当我尝试使用 localhost url 运行它时,我收到一条无法读取的消息。 我
代码如下。 IDE 的代码没问题,但 gradle 拒绝构建,并表示: TextAdapter is not abstract and does not override abstract metho
这个问题已经有答案了: Best practice for overriding classes / properties in ExtJS? (3 个回答) 已关闭 8 年前。 这两个覆盖有什么区别
我今天将 xcode 更新为 7。更新后,我正在处理的项目出现警告“覆盖成员函数但未标记为‘覆盖’”。由于我们的项目将“踩踏警告为错误”设置为true。我遇到了很多错误。 我仔细检查了“Other L
我试图将 Apple 的 ARKit 示例应用程序集成到我的应用程序中。由于 ARKit 只是一个附加功能,所以我需要支持较低版本的 iOS。我在所有 ARKit 示例应用程序类中添加了 @avail
我覆盖了类的 Equals() 来比较 Guid 类型的 ID 值。 然后 Visual Studio 警告: ... overrides Object.Equals(object o) but do
我正在尝试用 Java 中的 Runnable 对象创建一个基本线程。下面是我的代码: import java.lang.Thread; import java.lang.Runnable; publ
我有一个函数: int function(int a, int b = 1, int c = 2){ return a+b+c; } 我想将“c”变量的值设置为3,但不想设置“b”的值 在像
我正在尝试了解GAS的.code16行为。 在手册中,对于16位部分,对于32位操作数或指令,似乎会为指令编码生成一个66H操作数替代前缀。这是否意味着 .code16 movw %eax, %ebx
我正在尝试创建一个 JFrame,向 JFrame 添加一个 JLabel(image),但这需要我抛出 IOException,这会弄乱我的 main 方法中的 run() 。 谁能告诉我如何抛出异
我是一名优秀的程序员,十分优秀!