当我们创建一个从抽象类继承的类并且实现继承的抽象类时,为什么必须使用override关键字?
“为什么?” 这样的问题可能很难回答,因为它们含糊不清。我将假设您的问题是“在语言设计过程中可以提出哪些论据来证明override关键字是必需的位置?”
让我们先退后一步。在某些语言(例如Java)中,方法默认情况下是虚拟的,并且会自动覆盖。C#的设计师意识到了这一点,并认为这是Java中的次要缺陷。C#并非像某些人所说的那样“把愚蠢的部分删除掉的Java”,但是C#的设计师渴望从C,C ++和Java的有问题的设计点学习,而不是在C#中复制它们。
C#设计人员认为重写是可能的错误源。毕竟,这是一种更改现有经过测试的代码的行为的方法,这很危险。压倒不是偶然或偶然地要做的事情;它应该由认真思考的人设计。这就是为什么默认情况下方法不是虚拟的原因,以及为什么要求您说您要覆盖方法的原因。
这是基本的推理。现在,我们可以进行一些更高级的推理。
StriplingWarrior的答案为提出更高级的论据提供了很好的起点。派生类的作者可能不了解基类,可能打算制作新方法,并且我们不应该允许用户错误地重写。
尽管这一点是合理的,但存在许多反驳,例如:
- 派生类的作者有责任了解有关基类的所有知识!他们正在重用该代码,并且在重新使用该代码之前,应进行尽职调查以彻底了解该代码。
- 在您的特定情况下,虚拟方法是抽象的。不重写它将是一个错误,因此作者不太可能偶然创建实现。
然后让我们在这一点上做一个更高级的论证。在什么情况下派生类的作者可以因不知道基类的作用而被原谅? 好吧,考虑这种情况:
- 基类作者创建一个抽象基类B。
- 派生类的作者在另一个团队中使用方法M创建派生类D。
- 基类作者意识到扩展基类B的团队将始终需要提供方法M,因此基类作者添加了抽象方法M。
- 重新编译D类时,会发生什么?
我们要发生的是D的作者被告知相关的某些事情已经改变。发生变化的相关问题是,现在M是一个要求,并且必须重载它们的实现。 一旦我们知道可以从基类中调用它,DM可能需要更改其行为。 正确的做法是不要默默地说“哦,DM存在并且扩展了BM”。编译器要做的正确事情是fail,然后说“嘿,D的作者,检查您的假设不再有效,并在必要时修复代码”。
在您的示例中,假设on override是可选的,SayHello因为它覆盖了抽象方法。有两种可能性:(1)代码的编写者打算重写一个抽象方法,或者(2)重写方法被意外重写,因为其他人更改了基类,并且代码现在以某种微妙的方式出错。 如果override是可选的,我们无法区分这些可能性。
但是,如果override是需要那么我们就可以分辨三种情况。如果有可能的错误代码,然后override被丢失。如果它被有意覆盖然后override是本。而如果是故意不重写则new是现在。C#的设计使我们能够做出这些微妙的区别。
记住编译器错误报告需要阅读开发人员的思想 ; 编译器必须从错误的代码中推断出作者可能想到的是什么正确的代码,并给出错误以将它们指向正确的方向。我们可以给开发人员提供的线索越多,他们就在乎他们的想法,编译器就可以更好地报告错误,因此可以更快地找到并修复错误。
但更笼统地说,C#是为代码不断变化的世界而设计的。实际上,C#的许多“奇数”功能都存在,因为它们可以通知开发人员,因为当有效的假设由于基类发生变化而无效时,它们会通知开发人员。此类错误称为“脆性基类故障”,C#对此故障类有许多有趣的缓解措施。