如何使用多态+重载来改进这种方法以减少IS(类型检查)?
例如
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; } }
其中InheritedA
和InheritedB
是其inheritance的类。 实际上,还有更多的Inherited类, Add
根据其操作数的顺序和类型返回不同的结果。
我正在考虑使用多态和重载来重写它,但是,它变得相当复杂,我必须引入一个帮助方法来解决任一端的类型。
例如
InheritedA myA() { public override int Add(BaseClass next) { return next.AddTo(this); } }
现在我必须将AddTo
放入BaseClass
,并在inheritance的类中覆盖它。
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); }
有没有更好的方法呢?
您正在实现的模式称为双虚拟调度 。
单个虚拟分派根据接收器的运行时类型和参数的编译时类型选择调用哪个方法。 这是传统的虚拟调度:
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(); bM(a);
叫哪种方法? 未选择BM(Tiger)
和DM(Tiger)
; 我们根据参数的编译时类型拒绝它们,这是Animal。 但是我们根据是否是new B()
或new D()
来选择是在运行时调用BM(Animal)
还是DM(Animal)
new D()
。
双虚拟分派根据两件事的运行时类型选择调用哪个方法。 如果C#支持双虚拟调度,它不支持,那么运行时调度将转到BM(Tiger)
或DM(Tiger)
即使参数的编译时类型是Animal 。
但是,C#4支持动态调度。 如果你说
dynamic b = whatever; dynamic a = new Tiger(); bM(a);
然后,M的分析将完全在运行时使用b
和a
的运行时类型完成。 这明显变慢,但确实有效。
或者,如果您想进行双重虚拟调度并尽可能在编译时完成分析,那么标准的方法是实现访问者模式 ,您可以轻松地在互联网上查找。
正如评论中所建议的那样,如果你能够为每个派生分配一个常量值,那么你可以构建一个比我在这里描述的更清晰的实现,只需要一个名为Value
或类似的虚拟属性用于加成。
假设这不是一个选项,您可能希望考虑在基类级别预先计算结果,以描述您为每个组合分配的值。 随着类集的增长,这可能会崩溃并变得容易出错且乏味,所以我建议只考虑这个,如果你期望维持一个非常小的集合。
在我的基本示例中,我使用字典来保存集合并对组合进行硬编码。 根据你的评论,似乎没有任何算术的基本规则适用,所以我把它们作为限制留在这里。 如果结果值没有实际意义,并且您只是递增它,则可以考虑使用reflection构建结果集以拉取派生类并考虑每个组合。
public class BaseClass { private static readonly Dictionary addResults = new Dictionary(); 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 {}