【问题标题】:Method overloading: funnel call to derived class argument overload方法重载:对派生类参数重载的漏斗调用
【发布时间】:2014-04-15 03:04:32
【问题描述】:

好的,我正在尝试执行以下操作:

        protected bool ValidAdvert(Base item)
        {
            throw ThisIsAnAbstractClassException();
        }

        protected bool ValidAdvert(Derived1 item)
        {
            return ADerived1SpecificPredicate;
        }

        protected bool ValidAdvert(Derived2 item)
        {
            return ADerived2SpecificPredicate;
        }    

并在将基类传递给方法时调用该方法的派生类版本。基类是抽象的,所以理论上这应该是可能的?

在有人说在类本身上重载方法之前,方法内部的逻辑依赖于大量不同的条件,这些条件都没有关系,也没有直接与 Base/Derived 类相关(例如如登录状态等)

【问题讨论】:

    标签: c# overloading


    【解决方案1】:

    如果你觉得双重分派过于侵入,你可以通过反射调用方法并动态解决适当的重载。

    protected bool ValidAdvert(Base item)
    {
        if (item.GetType() == typeof(Base))
            throw new ThisIsAnAbstractClassException();
    
        Type type = typeof(CurrentClass);
    
        MethodInfo method = type.GetMethod("ValidAdvert",
                                           BindingFlags.Instance | BindingFlags.NonPublic,
                                           null,
                                           new Type[] { item.GetType() },
                                           null);
        return (bool)method.Invoke(this, new object[] { item });
    }
    
    protected bool ValidAdvert(Derived1 item)
    {
        return ADerived1SpecificPredicate;
    }
    
    protected bool ValidAdvert(Derived2 item)
    {
        return ADerived2SpecificPredicate;
    }
    

    这种模式称为MultipleDispatch,虽然通过反射调用方法比直接调用方法慢(如果方法每秒调用少于几百次也不会产生开销),它的好处是不需要像 Double Dispatch 模式那样修改你的类层次结构。

    当 c# 4.0 出现动态关键字时,这将变得更加容易:

    protected bool ValidAdvert(Base item)
    {
        if (item.GetType() == typeof(Base))
            throw new ThisIsAnAbstractClassException();
    
        dynamic dynamicThis = this;
    
        return (bool)dynamicThis.ValidAdvert(item as dynamic);
    }
    

    【讨论】:

    • 也很聪明,避免弄乱各种派生类(我希望只有2个!)。
    • 非常注意:您需要在返回时强制转换为 bool,并且 method.Invoke 需要一个 [] 对象,因此如果您尝试仅传递 1 就会出错 :) 谢谢!
    • @Ed -- 我认为将谓词保留在派生类中实际上是首选方法。将派生类特定的逻辑存储在基类中会在两者之间产生反向耦合。我根本不认为这是一件好事。基类不需要知道其派生类的任何信息。
    • @Ed 感谢您指出这些错误,我更新了答案。
    • @tvanfosson 如果该逻辑确实特定于对象层次结构,我同意在基类中具有 ValidAdvert 的抽象实现并在派生类中覆盖它会更清晰。但是,如果该逻辑与这些对象没有强耦合,那么使用多重调度并将所有逻辑放在同一个文件中而不是将其拆分为多个类会更简洁。
    【解决方案2】:

    使用double dispatch的完美场所:

    class Base
    {
        public abstract bool IsValid(IAdvertValidator validator);
    }
    
    class Derived1 : Base
    {
        public bool IsValid(IAdvertValidator validator)
        {
            return validator.ValidAdvert(this);
        }
    }
    
    class Derived2 : Base
    {
        public bool IsValid(IAdvertValidator validator)
        {
            return validator.ValidAdvert(this);
        }
    }
    
    interface IAdvertValidator
    {
        bool ValidAdvert(Derived1 d1);
        bool ValidAdvert(Derived2 d2);
    }
    

    【讨论】:

    • 模式的合理使用。另一种方法是从验证器公开验证方法,并在 IsValid 的实现中使用它们,将控制的实际验证流保留在类中。
    【解决方案3】:

    我不确定您将如何拥有一个实际上不是派生类实例的基类实例。由于基类是抽象的,因此无法实例化。我想它会匹配任何没有特定签名的派生类,但这似乎不是你想要的。

    【讨论】:

    • 是的,它始终是派生类之一,但是,从技术上讲,它也是基类,这意味着当我这样做时会调用基类方法 (List)i.Where(ValidateAdvert) ,例如。
    • 啊——我正在阅读您的代码示例,而不是您的问题。您实际上并不希望它引发异常,而是希望它调用其他方法之一。在那种情况下,我会接受@Anton 的建议或类似的建议。请注意,您可以在构造函数中提供验证器,这样您就不需要将其作为参数传递。我还使用了一个静态验证类来验证简单属性,从而无需保留对它的引用。但是,您需要小心这些,因为如果您不小心,它们会使测试变得更加困难。
    • 验证器是一个非常特定于页面和状态的类,因此不能很好地作为一个静态的,但是我认为它在反射方法中会很好,这是一个耻辱但默认情况下不支持此功能!
    猜你喜欢
    • 2014-10-31
    • 1970-01-01
    • 1970-01-01
    • 2014-06-01
    • 2015-12-30
    • 1970-01-01
    • 2013-05-18
    • 1970-01-01
    相关资源
    最近更新 更多