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

本文关键字:IS 检查 类型 方法 多态性 何使用 重载 | 更新日期: 2023-09-27 18:29:05

例如

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根据其操作数的顺序和类型返回不同的结果。

我正在考虑使用多态性和重载来重写它,然而,它变得相当复杂,我必须引入一个辅助方法来解决两端的类型。

例如

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

有更好的方法吗?

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

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

单个虚拟调度根据接收器的运行时类型和参数的编译时类型选择调用哪个方法。这是一个传统的虚拟调度:

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

调用哪种方法?不选择CCD_ 6和CCD_;我们拒绝它们是基于编译时类型的参数,即Animal。但我们根据whatevernew B()还是new D()来选择在运行时调用B.M(Animal)还是D.M(Animal)

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

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

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

然后,M的分析将在运行时使用ba的运行时类型进行。这明显较慢,但确实有效。

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

正如评论中所建议的那样,如果您能够为每个派生函数分配一个常数值,那么您可以通过使用一个名为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 {}