如果我有这两个类:
class A {}
class B : A {}
我制作一个List< A>但我想添加一个List< B>通过调用List< A> .AddRange(List< B>)但编译器拒绝:
Argument '1': cannot convert from 'System.Collections.Generic.List<A>'
to 'System.Collections.Generic.IEnumerable<B>
我完全理解,因为IEnumerable&lt; B&gt;不从IEnumerable&lt; A&gt;继承,其泛型类型具有继承。
我的解决方案是枚举List&lt; B&gt;并单独添加项目,因为List&lt; A&gt; .Add(A item)将与B项一起使用:
foreach(B item in listOfBItems)
{
listOfAItems.Add(item);
}
然而,这是非表达性的,因为我想要的只是AddRange。
我可以用
List<B>.ConvertAll<A>(delegate(B item) {return (A)item;});
但这是不必要的错综复杂和用词不当因为我没有转换,我正在施展。
问题:如果我要编写自己的类似List的集合,我会添加哪种方法,这样我就可以将B的集合复制到A的集合中作为单行类似于List&lt; A&gt; .AddRange(List&lt; B&gt;)和保留最大类型安全性。 (并且最大值我的意思是该参数既是集合又是类型的inhertance检查。)
答案 0 :(得分:8)
事实上,通用类型现在不是变体。在C#4.0中,IEnumerable <B
&gt;将可转换为IEnumerable <A
&gt; 如果B可通过参考转换转换为A 。有关此功能设计的一些详细信息,请参阅:
http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx
答案 1 :(得分:1)
这确实无法正常工作,因为.net中的泛型不支持协方差。
然而,你可以制作一个小帮手方法或类来克服这个问题。
如果您实现自己的列表类,则可以使用其他通用参数添加协方差:
class MyList<T> {
void AddRange<U>(IEnumerable<U> items) where U: T {
foreach (U item in items) {
Add(item);
}
}
}
答案 2 :(得分:1)
你不能这样做:
listOfAItems.AddRange(listOfBItems.Cast<A>());
答案 3 :(得分:1)
我能够使用LINQ ...
实现这一目标listOfAItems.AddRange(listOfBItems.Cast<A>());
答案 4 :(得分:1)
如果您发现自己处于泛型类型不变的情况下,以下扩展方法可以让您的生活更轻松:
public static void AddRange<TList,TOther>(this List<TList> list, IEnumerable<TOther> collection) where TOther: TList {
foreach(TOther e in collection) {
list.Add(e);
}
}
使用它作为扩展方法,而不必从List<T>
派生或在某个实用程序类中使用此方法,而不是简化使用。您也可以从推理中获益,因此这个以前无效的调用将在没有任何修改的情况下生效:
List<Animal> animals;
List<Dog> dogs;
animals.AddRange(dogs);
答案 5 :(得分:0)
我唯一能想到的就是这个
public class MyList<T> : List<T>
{
public void AddRange<Tother>(IEnumerable<Tother> col)
where Tother: T
{
foreach (Tother item in col)
{
this.Add(item);
}
}
}
调用它意味着做MyList&lt; A&gt; .AddRange&lt; B&gt;(MyList&lt; B&gt;)。如果参数不可枚举或者类型继承不能解决,那么它就会失败,因此它满足了我的问题的最大类型安全要求。