如何在独立Hasher类中模拟继承的多态方法调用机制?
问题描述
现有如下类层次结构:
public class Base { } public class Child1 : Base { } public class Child2 : Base { }
需要为这些类生成哈希值。如果在类内部实现哈希方法,可利用继承机制轻松实现多态调用:
public class Base { public virtual string GetHash() { ... } } public class Child1 : Base { public override string GetHash() { ... } } public class Child2 : Base { public override string GetHash() { ... } }
对于Base类型的实例foo,调用foo.GetHash()时,继承机制会自动将调用重定向到对应实例的实现(比如foo是Child1实例则调用Child1.GetHash)。
但出于关注点分离的要求,无法在类内部实现哈希逻辑,因此改用独立的Hasher类完成该任务。目前的实现采用条件链判断类型:
public class Hasher { public string GetHash(Base data) { if (data is Child1 c1) return GetHashChild1(c1); else if (data is Child2 c2) return GetHashChild2(c2); else return GetHashBase(data); } private string GetHashChild1(Child1 c) { ... } private string GetHashChild2(Child2 c) { ... } private string GetHashBase(Base b) { ... } }
现在需要找到一种新的实现方式,满足:
- 添加新的子类时,只需实现对应的哈希方法,无需修改
if-else/switch语句或维护字典等映射关系 - 方法调用能自动重定向到对应类型的哈希实现
同时疑问:泛型方法GetHash<T>(T ...)能否实现上述需求?
解决方案
1. 结合动态类型的重载方法(最直接实现)
利用C#的动态类型绑定特性配合方法重载,就能实现无需修改主逻辑的多态调用:
public class Hasher { // 入口方法,将参数转为dynamic触发运行时绑定 public string GetHash(Base data) { return GetHash((dynamic)data); } // Base类型的哈希实现 public string GetHash(Base baseObj) { // Base类哈希逻辑 return "..."; } // Child1类型的哈希实现 public string GetHash(Child1 child1) { // Child1哈希逻辑 return "..."; } // Child2类型的哈希实现 public string GetHash(Child2 child2) { // Child2哈希逻辑 return "..."; } }
优势:
- 添加新子类
ChildN时,仅需在Hasher中新增一个public string GetHash(ChildN childN)方法即可,完全不需要修改入口的GetHash(Base data)方法 - 运行时会自动根据实例的实际类型匹配对应的重载方法,效果和类内部多态一致
2. 泛型方法的局限性与改进
直接使用泛型方法public string GetHash<T>(T data)无法直接满足需求,因为C#的泛型方法重载是编译时绑定的。如果传入的是Base类型变量(实际是Child1实例),编译时会绑定到GetHash<Base>(Base data),而非GetHash<Child1>(Child1 data)。
但可以通过反射结合泛型实现自动匹配:
public class Hasher { public string GetHash(Base data) { var method = GetType().GetMethod("GetHash", new[] { data.GetType() }) ?? GetType().GetMethod("GetHash", new[] { typeof(Base) }); return (string)method.Invoke(this, new[] { data }); } public string GetHash(Base baseObj) { ... } public string GetHash(Child1 child1) { ... } public string GetHash(Child2 child2) { ... } }
这种方式通过反射在运行时找到对应类型的重载方法,效果和动态类型类似,但性能略低、代码更繁琐,不如动态类型方案简洁。
3. 访问者模式(需修改原类层次)
如果允许给原类层次添加少量代码(仅添加访问者接受逻辑,不包含哈希实现),可以使用访问者模式实现严格的编译时类型安全:
// 修改原类层次,添加访问者接受方法 public class Base { public virtual string Accept(IHasherVisitor visitor) { return visitor.Visit(this); } } public class Child1 : Base { public override string Accept(IHasherVisitor visitor) { return visitor.Visit(this); } } public class Child2 : Base { public override string Accept(IHasherVisitor visitor) { return visitor.Visit(this); } } // 定义访问者接口 public interface IHasherVisitor { string Visit(Base baseObj); string Visit(Child1 child1); string Visit(Child2 child2); } // 实现哈希访问者 public class Hasher : IHasherVisitor { public string GetHash(Base data) { return data.Accept(this); } public string Visit(Base baseObj) { ... } public string Visit(Child1 child1) { ... } public string Visit(Child2 child2) { ... } }
添加新子类时,需要:
- 在子类中重写
Accept方法 - 在
IHasherVisitor接口中新增Visit(ChildN childN)方法 - 在
Hasher中实现对应的Visit方法
这种方式的优点是编译时就能检查类型匹配,不会出现运行时找不到方法的情况,但需要修改原类层次,适合允许对原类做微小改动的场景。
内容的提问来源于stack exchange,提问作者Kjara
相关产品推荐
相关产品推荐

