C#调用继承类Visit方法时执行父类实现而非子类重载问题求解
问题原因
C#的方法重载决议发生在编译阶段,只会根据参数的编译时类型匹配对应的重载,不会参考运行时的实际类型。
你代码中Collide方法的ent1参数编译时类型是Entity,因此编译器会固定匹配参数为Entity的Visit重载,哪怕运行时ent1的实际类型是Player,也不会自动切换到Visit(Player)的重载,这就是为什么实际执行到了基类的Visit(Entity)方法。
修复方案
提供两种通用可扩展方案,新增实体类型时不需要修改现有逻辑:
方案1:使用dynamic延迟重载决议(实现最简单)
只需修改Collide方法的调用逻辑,将ent1强转为dynamic,即可让重载决议推迟到运行时,自动匹配ent1实际类型对应的Visit重载:
public virtual void Collide(Entity ent1, Entity ent2) { ent2.Visit((dynamic)ent1); }
该方案优势是改动极小,劣势是没有编译时类型检查,如果某个实体类型没有对应的Visit重载,会在运行时报错。
方案2:标准访问者模式实现(类型安全,推荐)
在基类中新增Accept抽象方法,每个子类实现该方法时传递自身的具体类型给Visit调用,从语法层面保证重载匹配正确:
- 首先修改
Entity基类:
public abstract class Entity { public virtual void Visit(Enemy enemy) { } public virtual void Visit(Player enemy) { } public virtual void Visit(Entity enemy) { } // 新增Accept抽象方法 public abstract void Accept(Entity visitor); public virtual void Collide(Entity ent1, Entity ent2) { // 改为调用Accept方法 ent1.Accept(ent2); } }
- 每个实体子类实现
Accept方法:
public class Enemy : Entity { public override void Visit(Enemy enemy) { } public override void Visit(Player enemy) { } // this的编译时类型为Enemy,会自动匹配Visit(Enemy)重载 public override void Accept(Entity visitor) { visitor.Visit(this); } } public class Player : Entity { public override void Visit(Enemy enemy) { } public override void Visit(Player enemy) { } // this的编译时类型为Player,会自动匹配Visit(Player)重载 public override void Accept(Entity visitor) { visitor.Visit(this); } }
该方案优势是所有重载匹配都有编译时检查,新增实体类型时只需在基类加对应的Visit重载、新子类实现Accept即可,不会出现运行时类型错误,适合实体类型较多的生产项目。
内容的提问来源于stack exchange,提问作者Semyon
相关产品推荐
相关产品推荐

