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

时间:2012-03-27 15:28:39

标签: 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;      
     }
}

其中InheritedAInheritedB是其继承的类。实际上,有更多的Inherited类,Add根据其操作数的顺序和类型返回不同的结果。

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

e.g。

InheritedA myA()
{
    public override int Add(BaseClass next)
    {
        return next.AddTo(this);
    }
}

现在我必须将AddTo放入BaseClass,并在继承的类中覆盖它。

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

有更好的方法吗?

2 个答案:

答案 0 :(得分: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);

调用哪种方法?未选择B.M(Tiger)D.M(Tiger);我们根据参数的编译时间类型拒绝它们,这是Animal。但我们会根据B.M(Animal)D.M(Animal)还是whatever来选择是在运行时致电new B()还是new D()

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

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

dynamic b = whatever;
dynamic a = new Tiger();
b.M(a);

然后,使用ba的运行时类型,完全在运行时完成对的分析。这明显变慢了,但确实有效。

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

答案 1 :(得分: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 {}