是否可以判断是否已在派生类中重写.NET虚方法?

时间:2009-05-08 14:15:30

标签: .net

提出这个问题的行为表明我对这个问题的处理方法不正确,所以我对直接解决问题的答案感兴趣,以及对我正在做的事情采取更清晰方法的答案。< / p>

考虑一个提供标准服务和功能集的基类,以及围绕这些服务和功能的一些结构。通过比喻,让我们考虑以下示例类:

public class ExampleBase
{
  public void Main()
  {
    // Do something
    PreValidate(); // Extensibility point for derived classes
    // Do something else
    PostValidate(); // Extensibility point for derived classes
    // Do something else 
  }

  protected virtual void PreValidate()
  {
  }

  protected virtual void PostValidate()
  {
  }
}

派生类现在可以覆盖这些虚方法以提供一些自定义逻辑。

以下是问题:基类是否有可能在运行时发现派生类是否可以自由地覆盖其中一个虚拟方法 之前调用虚方法?

(如果在调用该方法之后知道这个问题的答案就足够了,那么你可以用一个设置私有标志的方法替换基类中的空方法,表明该方法是 not 重写。但是,如果派生类在其重写的实现中调用base.PreValidate(),则可能会被欺骗。)

如果需要这种灵活性,也许最好的解决方案是使用完全不同的可扩展性机制?

8 个答案:

答案 0 :(得分:9)

虽然在其他答案中提到了解决此问题的最佳方法,但我回答问题标题(这将处理new问题):

var isOverridden = 
   new Action(MethodName).Method.DeclaringType != typeof(BaseClass);
// Replace `Action` with any conforming delegate type.

这个技巧使CLR方法调度机制为我们找到MethodInfo,而不是求助于反射来找到它。

答案 1 :(得分:2)

是否可以确定方法X是否在运行时被覆盖

是的,但正如您已经指出的那样,您可能不想这样做。您可以使用this.GetType()+反射魔法在派生类中查找覆盖当前基数的已定义方法。这是非常昂贵和棘手的,因为你必须考虑重载,深层次结构和其他有趣的通用问题。

修改

有关不使用Reflection的方法,请参阅Mehrdad的回答。

你应该做些什么?

您应该将此方法分为两部分。

  1. 你绝对依赖跑步的部分。使这成为非虚方法,因此派生类不会干扰它
  2. 可扩展性部分。使其成为从#1调用的虚拟保护方法。
  3. 例如

    protected void PreValidate() {
      .. my validation code
      PreValidateImpl();
    }
    
    protected virtual void PreValidateImpl() {
      // Nothing by default
    }
    

答案 2 :(得分:1)

你可以这样做:

public abstract class ExampleBase
{
  public void Main()
  {
    // Do something
    PreValidate(); // Extensibility point for derived classes
    // Do something else
    PostValidate(); // Extensibility point for derived classes
    // Do something else 
  }

  protected abstract void PreValidate();

  protected abstract void PostValidate();

}

这将强制所有派生类型覆盖方法。你可能最好不要在执行时检查这种事情 - 使用抽象类型和抽象方法,你可以让编译器为你提升,以确保你的类型的所有使用者都覆盖这些方法。

如果类型为abstract不起作用,那么您可以随时执行以下操作:

public class ExampleBase
{
    public void Main()
    {
        // Do something
        PreValidate(); // Extensibility point for derived classes
        // Do something else
        PostValidate(); // Extensibility point for derived classes
        // Do something else 
    }

    protected virtual void PreValidate()
    {
        throw new NotImplementedException();
    }

    protected virtual void PostValidate()
    {
        throw new NotImplementedException();
    }
}

但是这种方法可以将任何潜在的错误从编译时移到执行时,最好不惜一切代价避免。

答案 3 :(得分:1)

当然!你将不得不使用反射。

if (this.GetType().GetMethod("PreValidate").DeclaringType ==
        typeof(ExampleBase))
{
    // Not overridden
}
else
{
    // Overridden
}

保持我的反射有点慢,所以你不想执行它,例如,每秒一百万次。

答案 4 :(得分:1)

看起来你的PreValidate()和PostValidate()会更好地作为不同类型的扩展模型。提供者模型注入为代理或满足特定接口的完整对象可能更适合您希望能够在运行时检查这些服务的状态。

答案 5 :(得分:1)

您可以使用接口来模拟它:

public interface ISupportPreValidate
{
    void PreValidate();
}

public interface ISupportPostValidate
{  
    void PostValidate();
}

public class Base
{
    public void Validate()
    {
        if(this is ISupportPreValidate)
            ((ISupportPreValidate)this).PreValidate();

        // Validation logic

        if(this is ISupportPostValidate)
            ((ISupportPostValidate)this).PostValidate();            
    }
}

然后在Base - 派生类中实现所需的接口。

答案 6 :(得分:0)

问自己以下几点:

  1. 我会从ExampleBase创建一个对象吗?或者我是否总是从这个类继承并从继承的类创建一个对象?
  2. PreValidate和PostValidate是否需要任何派生类的方法?
  3. 重点是,从你的设计看,你应该真正创建一个抽象类,使用抽象方法,而不是虚方法。此外,根据您的描述,这似乎也是您想要获得的。

    public abstract class ExampleBase
    {
        public void Main()
        {
            // Do something
            PreValidate(); // Extensibility point for derived classes
            // Do something else
            PostValidate(); // Extensibility point for derived classes
            // Do something else 
        }
    
        protected abstract void PreValidate();
    
        protected abstract void PostValidate();
    }
    

    这样派生类就是FORCED来定义这些方法。

答案 7 :(得分:0)

JaredPar的替代方法很好,尽管事件是IMHO更清晰的可扩展性。

protected event ThreadStart OnPreValidate;

protected void PreValidate()
{
  .. my validation code
  if (null != OnPreValidate) {
    OnPreValidate();
  }
}