如何在列表存储对象时保留原始类型,避免转为公共父类?
解决C#列表存储子类实例时自动匹配重载方法的问题
你碰到的这个编译错误(CS1503)本质是静态类型检查的限制:虽然你的List<Super>里实际存的是Type1和Type2的实例,但编译时编译器只知道元素是Super类型,而Doer类并没有定义Do(Super)这个重载,所以找不到匹配的方法。
要实现「新增子类只加Do方法,不用修改循环逻辑」的需求,有两种常用方案,各有优劣,你可以根据场景选择:
方案一:用dynamic绕过静态类型检查
这是最简单快速的实现方式,只需要修改循环里的调用代码,把Super类型的变量转为dynamic,让运行时根据实际类型匹配对应的重载:
修改后的Main方法代码:
public static void Main(string[] args) { var list = new List<Super>() { new Type1 { Number = 1, Info = "infomatin" }, new Type2 { Number = 2, Prop = "propty" } }; foreach (var t in list) { Doer.Do((dynamic)t); // 关键:强制转为dynamic } }
优缺点:
- ✅ 代码改动极小,不需要修改基类和子类
- ❌ 失去编译时类型检查:如果新增了
Type3但忘记在Doer里加对应的Do(Type3)方法,编译时不会报错,只会在运行时抛出RuntimeBinderException
方案二:访问者模式(类型安全的面向对象方案)
如果追求编译时类型安全,符合开闭原则,推荐用访问者模式。需要给基类添加一个接受访问的方法,子类各自实现该方法来调用对应的Do重载:
步骤1:修改基类Super,添加抽象的Accept方法
public abstract class Super { public int Number { get; set; } public abstract void Accept(); // 新增抽象方法 }
步骤2:子类实现Accept方法
每个子类的Accept方法会主动调用Doer中对应自身类型的Do方法:
public class Type1 : Super { public string Info { get; set; } public override void Accept() => Doer.Do(this); // this是Type1类型,匹配Do(Type1) } public class Type2 : Super { public string Prop { get; set; } public override void Accept() => Doer.Do(this); // this是Type2类型,匹配Do(Type2) }
步骤3:修改循环逻辑调用Accept
public static void Main(string[] args) { var list = new List<Super>() { new Type1 { Number = 1, Info = "infomatin" }, new Type2 { Number = 2, Prop = "propty" } }; foreach (var t in list) { t.Accept(); // 调用子类的Accept方法,自动匹配对应Do重载 } }
优缺点:
- ✅ 编译时类型安全:如果新增子类但没实现
Accept,或者Doer没对应Do方法,编译时直接报错,避免运行时问题 - ✅ 符合开闭原则:新增子类只需要实现
Accept并在Doer加对应方法,不需要修改循环或基类逻辑 - ❌ 需要修改现有基类和子类的代码,初期改动比
dynamic大
两种方案都能实现你想要的效果:新增Type3时,只需要在Doer里添加public static void Do(Type3 arg)(方案一),或者再给Type3实现Accept方法(方案二),完全不用修改循环逻辑。
内容的提问来源于stack exchange,提问作者user3310334
相关产品推荐
相关产品推荐

