当我们在子类中实现抽象方法时,为什么需要在抽象方法之前使用override关键字?


9

当我们创建一个从抽象类继承的类并且实现继承的抽象类时,为什么必须使用override关键字?

public abstract class Person
{
    public Person()
    {

    }

    protected virtual void Greet()
    {
        // code
    }

    protected abstract void SayHello();
}

public class Employee : Person
{
    protected override void SayHello() // Why is override keyword necessary here?
    {
        throw new NotImplementedException();
    }

    protected override void Greet()
    {
        base.Greet();
    }
}

由于该方法在其父类中被声明为抽象方法,因此在父类中没有任何实现,因此为什么在此处必须使用关键字替代?




“重写修饰符是扩展或修改继承的方法,属性,索引器或事件的抽象或虚拟实现所必需的。” docs.microsoft.com/en-us/dotnet/csharp/language-reference/…–
gunr2171

2
因为如果不把它放在那儿,那是在“隐藏”该方法,而不是覆盖它。这就是为什么你会得到警告说,“如果这是你的本意,请使用newkeyword` ......你也会得到‘没有方法覆盖基类的方法’的错误。
罗恩·拜尔

@RonBeyer是使用虚拟方法的,但是使用抽象方法则无法编译。
Johnathan Barclay

Answers:


14

当我们创建一个从抽象类继承的类并且实现继承的抽象类时,为什么必须使用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#对此故障类有许多有趣的缓解措施。


4
感谢您的阐述。我总是很感激您的回答,这既是因为要有“内部”人员的权威声音是一件好事,又是因为您在简单而完整地解释事物方面做得很好。
StriplingWarrior

感谢您的深入解释!非常感谢。
psj01

好点!您能否列出您提到的其他一些缓解措施?
aksh1618

3

它是用来指定您是要覆盖父类中的另一个方法还是创建一个对于该类层次结构唯一的新实现。可以想象,程序员可能不知道父类中存在一种方法,该方法与他们在类中创建的方法具有完全相同的签名,这可能会带来一些令人讨厌的惊喜。

虽然在非抽象子类中必须重写抽象方法是正确的,但是C#的编写者可能认为最好还是清楚地说明您要执行的操作。


这是理解语言设计团队推理的一个良好开始。我添加了一个答案,该答案显示了团队如何从您在此处表达的想法开始,但又迈出了一步。
埃里克·利珀特

1

因为abstractmethod是没有实现的虚拟方法,所以根据C#语言规范,意味着抽象方法隐式为虚拟方法。并且override用于扩展或修改抽象或虚拟实现,如您在此处看到的

更确切地说,您可以使用虚拟方法来实现某种后期绑定,而抽象方法则强制类型的子类显式地覆盖该方法。这就是问题所在,如果方法是virtual,它可以被覆盖,当它是一个abstract-它必须被重写


1
我认为问题不在于其作用,而在于为何必须明确。
Johnathan Barclay

0

为了增加@StriplingWarrior的答案,我认为它的语法也与在基类中覆盖虚拟方法一致。

public abstract class MyBase
{
    public virtual void MyVirtualMethod() { }

    public virtual void MyOtherVirtualMethod() { }

    public abstract void MyAbtractMethod();
}

public class MyDerived : MyBase
{
    // When overriding a virtual method in MyBase, we use the override keyword.
    public override void MyVirtualMethod() { }

    // If we want to hide the virtual method in MyBase, we use the new keyword.
    public new void MyOtherVirtualMethod() { }

    // Because MyAbtractMethod is abstract in MyBase, we have to override it: 
    // we can't hide it with new.
    // For consistency with overriding a virtual method, we also use the override keyword.
    public override void MyAbtractMethod() { }
}

因此,可以设计C#以便您不需要重写关键字来覆盖抽象方法,但是我认为设计人员认为这样做会造成混淆,因为它与覆盖虚拟方法不一致。


回复:“设计人员认为这会造成混乱,因为它与覆盖虚拟方法不一致”,是的,但还有更多。假设您有一个带有虚拟方法M的基类B和一个具有重写的派生类D。现在假设B的作者决定使M抽象。这是一个重大变化,但也许他们这样做。问题:是否应要求D的作者删除override 我认为大多数人都会同意强迫D的作者进行不必要的代码更改是荒谬的。他们的班很好!
埃里克·利珀特
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.