代码之家  ›  专栏  ›  技术社区  ›  Daniel Fortunov

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

  •  3
  • Daniel Fortunov  · 技术社区  · 15 年前

    问这个问题的行为表明我处理这个问题的方法是错误的,所以我对直接解决问题的答案感兴趣,也对建议对我正在做的事情采取更清洁的方法的答案感兴趣。

    考虑一个基类,它提供一组标准的服务和功能,以及这些服务和功能周围的一些结构。通过隐喻,我们来考虑下面的示例类:

    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()
      {
      }
    }
    

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

    这是个问题 :如果派生类具有重写这些虚拟方法之一的自由,则基类是否可以在运行时发现, 之前 调用虚拟方法?

    (如果知道这个问题的答案就足够了 之后 调用该方法,然后您可以用设置私有标志的方法替换基类中的空方法,该标志将指示该方法是 重写的但是,如果派生类调用 base.PreValidate() 在其重写的实现中。)

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

    8 回复  |  直到 15 年前
        1
  •  1
  •   Anton Gogolev    15 年前

    您可以通过以下接口来模拟:

    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 -派生类。

        2
  •  9
  •   Mehrdad Afshari    15 年前

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

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

    这一技巧使得clr方法调度机制能够找到 MethodInfo 对我们来说,不是通过反思来找到它。

        3
  •  2
  •   Mehrdad Afshari    15 年前

    是否可以确定方法x在运行时是否被重写?

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

    编辑

    要想在不使用反射的情况下实现这一点,请参见Mehrdad的回答。

    你应该做些什么?

    您应该将这个方法分为两部分。

    1. 你完全依赖跑步的那部分。将此方法设为非虚拟方法,以便派生类不能干扰它
    2. 可扩展性部分。使其成为从1调用的虚拟保护方法。

    例如

    protected void PreValidate() {
      .. my validation code
      PreValidateImpl();
    }
    
    protected virtual void PreValidateImpl() {
      // Nothing by default
    }
    
        4
  •  1
  •   Andrew Hare    15 年前

    你可以这样做:

    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();
        }
    }
    

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

        5
  •  1
  •   Tamas Czinege    15 年前

    当然!你必须运用反省。

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

    保持在我的思考是有点慢,所以你不想执行,比如说,一百万次每秒。

        6
  •  1
  •   Tetsujin no Oni    15 年前

    似乎prevalidate()和postvalidate()更适合作为不同类型的扩展模型。将这些作为委托或满足特定接口的完整对象注入的提供者模型可能更适合您希望能够在运行时检查这些服务的状态。

        7
  •  0
  •   Joseph    15 年前

    问自己以下问题:

    1. 我会从ExampleBase创建一个对象吗?或者我总是从这个类继承并从继承的类创建一个对象?
    2. prevalidate和postvalidate是否是任何派生类的必需方法?

    重点是,从您的设计来看,您应该真正创建一个抽象类,使用抽象方法,而不是虚拟方法。而且,从你的描述来看,这也是你想要达到的目标。

    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();
    }
    

    通过这种方式,派生类被强制定义这些方法。

        8
  •  0
  •   Lachlan Roche    15 年前

    Jaredpar的替代方法是好的,尽管事件在可扩展性方面比较干净。

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