如何使用多态+重载来改进这种方法,以减少IS(类型检查)?

col*_*ang 6 c# polymorphism overloading

例如

BaseClass MyBase()
{
    public int Add(BaseClass next)
    {
        if (this is InheritedA && next is InheritedA)
            return 1;
        else if (this is InheritedA && next is InheritedB)
            return 2;
        else if (this is InheritedB && next is InheritedA)
            return 3;
        else if (this is InheritedB && next is InheritedB)
            return 4;      
     }
}
Run Code Online (Sandbox Code Playgroud)

where InheritedA,InheritedB是它的继承类.实际上,还有更多的Inherited类,并Add根据其操作数的顺序和类型返回不同的结果.

我正在考虑使用多态和重载来重写它,但是,它变得相当复杂,我必须引入一个帮助方法来解析任一端的类型.

例如

InheritedA myA()
{
    public override int Add(BaseClass next)
    {
        return next.AddTo(this);
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我不得不把AddToBaseClass,并在继承的类中重写它.

InheritedA myA()
{
    public override int AddTo(InheritedA next) { return 1; }
    public override int AddTo(InheritedB next) { return 3; }
}

BaseClass myBase()
{
    public abstract int Add(BaseClass next);
    public abstract int AddTo(InheritedA next);
    public abstract int AddTo(InheritedB next);
}
Run Code Online (Sandbox Code Playgroud)

有没有更好的方法呢?

Eri*_*ert 9

您正在实现的模式称为双虚拟调度.

单个虚拟调度选择的基础上调用哪个方法运行时类型的接收器和编译时间类型的参数.这是传统的虚拟调度:

abstract class Animal {}
class Tiger : Animal {}
class Giraffe : Animal {} 
class B
{
    public virtual void M(Tiger x) {}
    public virtual void M(Animal x) {}
}
class D : B
{
    public override void M(Tiger x) {}
    public override void M(Animal x) {}
}
...
B b = whatever;
Animal a = new Tiger();
b.M(a);
Run Code Online (Sandbox Code Playgroud)

叫哪种方法?B.M(Tiger)而且D.M(Tiger)没有被选中; 我们根据参数的编译时类型拒绝它们,这是Animal.但是我们选择是否调用B.M(Animal)D.M(Animal)在基于是否运行whatevernew B()还是new D().

双虚拟分派根据两件事的运行时类型选择调用哪个方法.如果C#支持双虚拟分派,它不支持,那么运行时分派将转到B.M(Tiger)或者D.M(Tiger)即使参数的编译时类型是Animal.

但是,C#4支持动态调度.如果你说

dynamic b = whatever;
dynamic a = new Tiger();
b.M(a);
Run Code Online (Sandbox Code Playgroud)

然后M的分析会做完全在运行时使用运行时类型ba.这明显变慢,但确实有效.

或者,如果您想进行双重虚拟调度并尽可能在编译时完成分析,那么标准的方法是实现访问者模式,您可以轻松地在互联网上查找.


Jes*_*ire 1

正如评论中所建议的,如果您能够为每个派生值分配一个常量值,那么您可以通过仅使用一个名为Value或类似的虚拟属性来构建一个比我在这里描述的更清晰的实现,该虚拟属性用于添加。

假设这不是一个选项,您可能希望考虑在基类级别预先计算结果来描述您为每个组合分配的值。随着类集的增长,这可能会崩溃并变得容易出错且乏味,因此我建议仅在您期望维护一个非常小的集合时才考虑这一点。

在我的基本示例中,我使用字典来保存集合并对组合进行硬编码。从你的评论来看,算术的基本规则似乎都不适用,所以我在这里将它们作为约束排除在外。如果结果值没有实际意义并且您只是递增它,则可以考虑使用反射来构建结果集以拉取派生类并考虑每种组合。

public class BaseClass
{
  private static readonly Dictionary<int, int> addResults = new Dictionary<int, int>();

  static BaseClass()
  {
    addResults.Add(CreateKey(typeof(ChildA), typeof(ChildA)), 1);
    addResults.Add(CreateKey(typeof(ChildA), typeof(ChildB)), 2);
    addResults.Add(CreateKey(typeof(ChildB), typeof(ChildA)), 3);
    addResults.Add(CreateKey(typeof(ChildB), typeof(ChildB)), 4);
  }

  public static int CreateKey(Type a, Type b)
  {
    return (String.Concat(a.Name, b.Name).GetHashCode());
  }

  public int Add(BaseClass next)
  {
    var result = default(int);

    if (!addResults.TryGetValue(CreateKey(this.GetType(), next.GetType()), out result))
    {
      throw new ArgumentOutOfRangeException("Unknown operand combination");
    }

    return result;
  }
}

public class ChildA : BaseClass {}
public class ChildB : BaseClass {}
Run Code Online (Sandbox Code Playgroud)