使用AddRange添加子类的集合

时间:2009-05-04 13:36:42

标签: c# .net-3.5 inheritance iteration

如果我有这两个类:

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检查。)

6 个答案:

答案 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;)。如果参数不可枚举或者类型继承不能解决,那么它就会失败,因此它满足了我的问题的最大类型安全要求。