C# 如何检测虚方法是否在c中被重写#

C# 如何检测虚方法是否在c中被重写#,c#,virtual-functions,C#,Virtual Functions,是否可以确定虚拟方法是否已被重写: class ABase { public void DoSomething(object p) { p.Process(); if( /* DoSomethingExtra is implemented */ ) DoSomethingExtra(p); } public virtual void DoSomethingExtra(object p) { } } class ADerived { publi

是否可以确定虚拟方法是否已被重写:

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#中,可以声明一个虚拟方法而不实现它

这是不可能的。您可以将一个方法声明为抽象,但是如果该方法是虚拟的,那么它将有一些实现(即使该实现实际上是一个空操作)

上面的代码报告错误
error 1'ABase.DoSomethingExtra(object)'必须声明一个主体,因为它没有标记为抽象、外部或部分

处理此问题的典型方法是使用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()什么都不做,为什么不调用它呢)。我向你保证我有正当的理由。有什么想法吗

没有直接的方法来确定当前实例是否覆盖了虚拟方法。这可能需要一些非常讨厌的、不可维护的代码,比如检查,看看有什么


尽管如此,我还是强烈质疑这里的设计目标。您的问题基本上是要求使用一种特定的方法来违反,这是面向对象编程的核心原则之一。这会降低代码的性能和可维护性…

有几个选项:

如果派生类必须实现
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;

更新:提供的链接过于冗长,但也不完整。上面的版本也适用于受保护的方法和虚拟属性(这就是为什么您使用
GetMember
而不是更严格的
GetMethod
),这两种方法都没有链接地址。

反射是这个问题的好答案

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

+1只是因为它看起来很粗糙,所以没有任何解释就投了反对票!当一个空的实现也能正常工作时,这个标志似乎是不必要的信息。要求设置一个标志将大大降低可维护性,因为方法重写预期会起作用…@Reed我同意,这就是为什么我在结尾添加了一条注释,指出空实现是最好的。按照这些思路,在某些情况下,您可以翻转逻辑,以便重写器不必“记住”设置标志:如果您控制所有调用站点,尤其是它们都来自基类本身,那么您可以采用覆盖器从不调用虚拟基类的约定。然后,在每次调用之前设置一个私有标志,并始终在虚拟函数库中清除它。如果在调用返回时仍然设置,则它被覆盖。如前所述,这类事情是脆弱的,但这样做可以使舷外清洁。请注意,这不是所描述的线程安全。也许您可以澄清这个问题,因为编译器不允许未实现的虚拟方法?您的代码无法编译。@James Gaunt,您当然是正确的。出于某种原因,VisualStudio没有给我任何语法错误,这让我相信它是可以接受的代码。然而,当我运行代码时,它失败了。我已经纠正了这个问题。啊!现在你问了一个完全不同的问题。检查此反射将是在运行时执行此操作的唯一方法。这应该附带一个健康警告,但是,从OOP的角度来看,这应该被认为是一个非常糟糕的想法(tm)。谢谢James,这就是我想要的。如果你把它作为一个答案,我会给你信用。(你说得对,我一开始确实问了一个错误的问题:)任何从基类派生的东西都必须实现这个方法,因此如果你的代码被调用,这个方法就会被重写;当然,除非你所说的“实现”是指其他东西?[编辑:好吧,澄清一下,任何已经实例化的、从基类派生的东西…]
你的问题基本上是在问一种违反Liskov替换原则的具体方法。ADerived仍然是ABase的完美替代品。根据James Gaunt的评论,有一种非常干净的方法可以使用反射来确定虚方法的实现来自哪个类。@threed:您正试图根据runti更改行为