C#中List多态实现疑问:构造函数中如何转换IList<B>为IList<A>
泛型集合的协变问题与构造函数场景下的解决方案
为什么集合转换失败,单个对象却可以?
- 单个对象的转换是引用转换:由于
B继承自A,B的实例本身就是A的实例,引用可以直接向上转换,这是继承体系的基本特性,完全符合类型安全。 IList<T>是不变泛型接口:C#中只有标记了out关键字的泛型接口才支持协变(比如IEnumerable<out T>),IList<T>没有这个标记。如果允许IList<B>直接转IList<A>,你可以通过alist.Add(new A())往原本只能存储B实例的集合中添加A实例,这会直接破坏集合的类型安全,因此C#禁止这种转换。
构造函数场景的限制
C#规定派生类构造函数必须优先调用基类构造函数,这导致你无法先对IList<B>做转换处理,再传递给基类构造函数——直接强制转换(IList<A>)param会编译报错,甚至运行时抛出类型转换异常。
符合需求的简洁解决方案
结合你需要保证元素顺序、支持动态长度的要求,有两种更简洁的实现方式:
方案1:调整基类构造函数参数类型(优先推荐)
如果基类构造函数可以接受IEnumerable<A>类型的参数,直接传入param即可:
public X(IList<B> param) : base(param) {}
因为IEnumerable<out T>是协变接口,IList<B>实现了IEnumerable<B>,可以隐式转换为IEnumerable<A>,既保证类型安全,又无需额外转换操作。
方案2:直接构造List<A>实例
如果基类构造函数必须接收IList<A>,可以利用List<T>的构造函数直接转换:
public X(IList<B> param) : base(new List<A>(param)) {}
List<A>的构造函数支持接收IEnumerable<A>参数,而IList<B>可以通过协变隐式转换为IEnumerable<A>,这个写法比你当前用的Select更简洁,同时完全保留元素顺序,且生成的List<A>支持动态调整长度。
内容的提问来源于stack exchange,提问作者user19335768
相关产品推荐
相关产品推荐

