为什么C#不允许调用base.SomeAbstractMethod

Jac*_*rby 6 c# oop abstract-class

以下是一些讨论代码

  abstract class ClassA
  {
    public abstract void StartProcess();
  }

  class ClassB : ClassA
  {
    public override void StartProcess()
    {      
      Console.WriteLine("ClassB: Render");
    }
  }

  class ClassC : ClassA
  {
    public override void StartProcess()
    {
      base.StartProcess();//This is where the compiler complains
      Console.WriteLine("ClassC: Render");
    }
  }
Run Code Online (Sandbox Code Playgroud)

在每个人都跳下我的喉咙之前,让我说我完全清楚它为什么没有.但有些情况下,能够这样做是有意义的,并且可以防止必须将基类的方法声明为虚拟但具有空实现.

来自Delphi背景,我们可以在Delphi中完成这个并在我们的类设计中使用它.如果你错误地在基类上调用抽象方法(在运行时),你就会得到一个"抽象错误".

然后我希望(Delphi)编译器检查我之前!现在我希望(C#)编译器允许我这样做!这有多奇怪?

问题:编译器/ Jitter难道不能忽略这样的调用并发出警告而不是错误吗?别人看到/感受到这种痛苦吗?

我需要的情况如下:ClassA是库的一部分(无法控制此类)生成ClassC(类似于编译ASP.NET页面或编译Razor View的方式).

但是库的用户可以定义ClassB,然后ClassC将从ClassB而不是ClassA(当它生成时)下降.类似于ASP.NET页面通常从System.Web.UI.Page下载的方式,但是如果您已经定义了自己的"基础"页面和应用程序中的其他页面,那么现在从您的基页继承,那么生成的类将从您的基页下降(转而来自System.Web.UI.Page).

我希望这部分是清楚的.然后看看我提供的代码,我无法获得ClassC的实例来调用ClassB的实现,因为代码gen不知道包含base.StartProcess().

编辑 似乎有些人并不完全得到我写的东西.因此,假设您正在编写代码生成部分,该部分生成从ClassA下降的ClassC.好吧,因为该方法是anstract(在ClassA中),所以无法生成调用StartProcess()的代码行(因为编译器不允许它).因此,如果有人定义了ClassB,代码生成仍然不会调用base.StartProcess().这实际上是ASP.NET MVC视图中发生的事情.

理想情况下,我希望编译器忽略它.它忽略了很多东西,例如在null引用上调用dispose.

我正在尝试进行讨论,而不是鼓吹......

EDIT2 假设我们有一个层次结构,如上面的代码所示,它工作正常.我们现在的机会是,基类ClassA可能有一个实现(将来)StartProcess()后代将调用它.今天实现这一目标的唯一方法是将方法定义为无虚体.但这对我来说感觉有点蠢.

Jon*_*eet 10

base.StartProcess()如果被宣布为抽象的话,怎么可能有意义呢?不可能有一个实现调用,因此编译器禁止它.

就个人而言,我喜欢在编译时看到错误,而不是在执行时看到错误或者让JITter 忽略我专门制作的调用.如果它返回了您赋给变量的值,该怎么办?如果该方法不存在,该变量值应该是多少?

如果ClassC将从ClassB派生,那么你就不会遇到问题 - 因为你不会调用抽象基本方法.但是你的代码声明它直接来自ClassA,而不是ClassB.如果生成ClassC,则应该生成它以从ClassB派生,这样就可以了.

我个人认为编译器在这里做的正确.

编辑:只是为了让它完全清楚,我相信适当的解决方案是:

  • 如果您希望能够base.M()从任何派生类调用,则应将其设置为具有无操作实现的虚方法,而不是抽象方法.
  • 如果你有一个代码生成器,它应该base.M()只在它生成一个基类具有实现的类的情况下生成一个调用M,那么由代码生成器来实现这一点 - 语言不应该让其他人都受苦(通过将错误报告延迟到执行时间,或者更糟糕的是仅通过执行无操作来吞下该错误)仅仅因为一个工具被错误地写入.

我认为的缺点或者使其成为一个执行时错误调用一个抽象基方法使其成为一个无操作比在问题描述的问题变得更糟.

现在,一个有趣的语言功能,可以潜在有用这里会迫使覆盖之前或之后重写调用基实现虚方法的想法...以类似的方式在派生类的构造函数怎么总是有直接或通过另一个构造函数调用基类中的构造函数.我强烈怀疑这样一个特性的复杂性(返回值会发生什么?如何在语义之前/之后使用指定?异常怎么样?)会超过好处.在简单的类层次结构中,模板方法模式可以以更简单的方式执行相同的任务.

  • +1为编译器不应编译垃圾的想法.这不是我们在这里谈论的Perl. (6认同)