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

本文关键字:方法 是否 覆盖 何检测 检测 | 更新日期: 2023-09-27 18:07:10

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

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(),因为它什么都不做)。我向你保证我有正当理由这么做。什么好主意吗?

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

所以在c#中可以声明一个虚方法而不实现它。

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

上面的代码报告错误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(),因为它什么都不做)。我向你保证我有正当理由这么做。什么好主意吗?

没有直接的方法来确定当前实例是否已经覆盖了您的虚拟方法。这可能需要一些非常讨厌的、不可维护的代码,比如通过反射检查方法体声明类型,看看那里有什么。

话虽如此,我强烈质疑这里的设计目标。你的问题基本上是要求一种特定的方式来违反Liskov替代原则,这是面向对象编程的核心原则之一。这将使你的代码性能下降,更难以维护…

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

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(),除非它没有被覆盖。

检查一下:检测方法是否被反射(c#)覆盖

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

有几个选项:

如果派生类必须实现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; } }
}
但是,这个解决方案有点脆弱,因为派生类在重写方法时可能会忘记重写属性。我认为有一个什么都不做的基本实现是最简单的方法。

这是得到你想要的东西的最简单的方法:

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

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