如何检测c#中是否覆盖了虚方法

时间:2011-08-29 18:00:46

标签: c# virtual-method

是否可以确定是否已覆盖虚拟方法:

class ABase {

  public void DoSomething(object p)
  {
    p.Process();
    if( /* DoSomethingExtra is implemented */ )
      DoSomethingExtra(p);
  }
  public virtual void DoSomethingExtra(object p) { }

}

class ADerived {
  public override void DoSomethingExtra(object p)
  {
    p.ProcessMore();
  }
}

我意识到这个例子看起来很愚蠢(例如,你为什么不调用DoSomethingExtra(),因为它没有做任何事情)。我向你保证,我有一个合法的案例。有什么想法吗?

5 个答案:

答案 0 :(得分:10)

  

因此,在C#中,可以在不实现的情况下声明虚方法。

这是不可能的。您可以将方法声明为 abstract ,但如果该方法是虚拟的,则它将具有一些实现(即使实现实际上是null操作)。

上面的代码报告错误Error 1 'ABase.DoSomethingExtra(object)' must declare a body because it is not marked abstract, extern, or partial

处理此问题的典型方法是使用null op实现声明方法,并调用它:

class ABase {
  public void DoSomething(object p)
  {
    p.Process();
    DoSomethingExtra(p); // Always call
  }
  public virtual void DoSomethingExtra(object p)
  {
      // Do nothing here
  }
}

编辑:既然您的问题已经过编辑,我会为您提供与您的编辑相关的更多信息,特别是:

  

我意识到这个例子看起来很愚蠢(例如,你为什么不调用DoSomethingExtra(),因为它没有做任何事情)。我向你保证,我有一个合法的案例。有什么想法吗?

没有直接的方法来确定当前实例是否覆盖了您的虚方法。这可能需要一些非常讨厌,不可维护的代码,例如检查method body declaring type via reflection以查看其中的内容。

话虽如此,我强烈质疑这里的设计目标。你的问题基本上是要求一种违反Liskov Substitution Principle的特定方式,这是面向对象编程的核心原则之一。这将导致您的代码性能降低,维护性降低......

答案 1 :(得分:5)

反思是这个问题的一个很好的答案。

using System.Reflection;

Type classType = typeof(ADerived);
MethodInfo method = classType.GetMethod("DoSomethingExtra");
if (method.DeclaringType == typeof(ABase))
    Console.WriteLine("DoSomethingExtra not overridden.");
else
    Console.WriteLine("DoSomethingExtra is overridden by " + method.DeclaringType.Name);

我希望你会发现这很有用。

一旦我实现了一个特殊的对象查看器,当对象未知时,我将使用ToString(),除非它没有被覆盖。

答案 2 :(得分:2)

有几个选择:

如果派生类必须实现DoSomethingExtra(),则将方法和类声明为abstract。这将迫使具体派生类具有实现。然后,您可以从基类代码中调用DoSomethingExtra(),知道实现将存在。

abstract class ABase {

  public void DoSomething(object p)
  {
    p.Process();
    DoSomethingExtra(p);
  }

  public abstract void DoSomethingExtra(object p);
}

如果派生类可能实现该方法,那么只需在基类中包含一个默认实现,如果没有派生实现可用,则会调用该实现。

另一个选择是有一个派生类可以设置的标志,指示他们想要要做的额外事情:

class ABase {

  public virtual bool ShouldDoSomethingExtra { get { return false; } }

  public void DoSomething(object p)
  {
    p.Process();
    if(ShouldDoSomethingExtra)
      DoSomethingExtra(p);
  }
  public virtual void DoSomethingExtra(object p) { // Empty in base }
}

class ADerived {
  public override void DoSomethingExtra(object p)
  {
    p.ProcessMore();
  }

  public override bool ShouldDoSomethingExtra { get { return true; } }
}

但是,这个解决方案有点脆弱,因为派生类在重写方法时可能会忘记覆盖属性。我认为有一个无操作的基础实现是最简单的方法。

答案 3 :(得分:2)

查看Detect if a method was overridden using Reflection (C#)

反射是在运行时执行此操作的唯一方法。这应该带有健康警告但是,从OOP的角度来看,它应该被认为是一个非常糟糕的想法(tm)。基类通常不应该知道或关心如何实现派生类。

答案 4 :(得分:1)

这是获得所需内容的最简单方法:

var isDerived = typeof(ADerived).GetMember("DoSomethingExtra", 
                   BindingFlags.NonPublic 
                 | BindingFlags.Instance 
                 | BindingFlags.DeclaredOnly).Length == 0;

更新:提供的链接here比必要的更详细,但也不完整。上面的版本也适用于受保护的方法和虚拟属性(这就是您使用GetMember而不是限制性更强的GetMethod)的原因,这两者都不是链接地址。