为什么C#支持抽象成员的抽象覆盖?

时间:2017-09-13 12:41:19

标签: c# abstract

在浏览一些遗留代码时,我很惊讶地遇到了一个抽象的抽象覆盖,它本身就是抽象的。基本上是这样的:

public abstract class A
{
    public abstract void DoStuff();    
}

public abstract class B : A
{
    public override abstract void DoStuff();  // <--- Why is this supported?
}

public abstract class C : B
{
    public override void DoStuff() => Console.WriteLine("!");    
}

编译器始终可用的虚拟或抽象成员之间的区别是什么?为什么C#支持这个?

(此问题与What is the use of 'abstract override' in C#?不重复,因为类DoStuff中的A - 方法不是虚拟的,也是抽象的。)

1 个答案:

答案 0 :(得分:17)

澄清问题:问题不是“为什么抽象覆盖合法?” An existing question处理这个问题。 (另请参阅my blog post。)

相反,问题是“当被覆盖的方法也是抽象的时,为什么抽象覆盖是合法的?”

有几个论据赞成不在这里给出错误。

  1. 这是无害的。

  2. 要发出错误,编译器团队中的某个人必须考虑到这种情况并认为值得花时间设计该功能,验证它不会导致与其他功能的任何不良交互,写详细的规范,实现,测试,编写文档,将错误消息翻译成二十几种语言,翻译文档,并永久维护该功能。证明这些成本的合理性是什么?我没有看到。

  3. 每当C#中的某些内容看起来很奇怪时,请问自己如果基类是由不在我的团队中的人拥有并且他们想编辑它会怎么样?

  4. 例如,考虑您的方案的变体:

    public abstract class Z 
    {
      public abstract void DoStuff();
    }
    
    public class A : Z
    {
        public override void DoStuff() 
        { 
          throw new NotImplementedException(); 
        }
    }
    
    public abstract class B : A
    {
        public override abstract void DoStuff();
    }
    

    A级的作者意识到他们犯了一个错误,A和DoStuff应该是抽象的。制作一个抽象类是一个突破性的变化,因为任何说“新A()”的人现在都错了,但是他们验证他们的组织中没有一个客户团队创建了新的A.同样,现在调用base.DoStuff错误。但同样,没有。

    因此,他们将代码更改为您的A类版本。 B类现在是否有编译时错误?为什么? B.没有错。

    C#的许多功能看似奇怪,因为设计师认为脆弱的基类是一个重要的场景。

    1. 最后,我们应该考虑覆盖方法的前提条件。重写的方法必须可以通过覆盖代码访问,覆盖的代码必须是派生类型,并且重写的方法必须首先是虚方法。这些要求有明显的理由。引入另一个要求 - 覆盖要求覆盖和覆盖的方法中只有一个是抽象的 - 没有原则性的原因。