1

如果我有这两个类:

class A {}
class B : A {}

我创建了一个 List<A> 但我想通过调用 List<A>.AddRange(List<B>) 添加一个 List<B> 但编译器拒绝:

Argument '1': cannot convert from 'System.Collections.Generic.List<A>'
to 'System.Collections.Generic.IEnumerable<B>

我完全理解,因为 IEnumerable<B> 不是从 IEnumerable<A> 继承的,它的泛型类型具有继承性。

我的解决方案是枚举 List<B> 并单独添加项目,因为 List<A>.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<A>.AddRange(List <B>)保持最大的类型安全性。(最多我的意思是参数既是集合又是类型继承检查。)

4

6 回答 6

8

事实上,泛型类型现在不是变体。在 C# 4.0 中,如果 B 可通过引用转换转换为 A<B ,则 IEnumerable > 将转换为 IEnumerable <A> 。有关此功能设计的一些详细信息,请参见:

http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx

于 2009-05-04T16:09:46.310 回答
1

不幸的是,这不起作用,因为 .net 中的泛型(尚)不支持协方差。

但是,您可以创建一个小的帮助方法或类来克服这个问题。

如果您实现自己的列表类,则可以使用附加的通用参数添加协方差:

class MyList<T> {
    void AddRange<U>(IEnumerable<U> items) where U: T {
        foreach (U item in items) {
            Add(item);
        }
    }
}
于 2009-05-04T13:39:16.607 回答
1

你不能这样做:

listOfAItems.AddRange(listOfBItems.Cast<A>());
于 2009-05-04T13:41:40.220 回答
1

我能够使用 LINQ 实现这一目标......

listOfAItems.AddRange(listOfBItems.Cast<A>());
于 2013-06-17T13:09:40.810 回答
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);
于 2016-03-03T08:01:11.000 回答
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<A>.AddRange<B>(MyList<B>)。如果参数不可枚举或者类型继承没有成功,则此操作失败,因此它满足了我的问题的最大类型安全要求。

于 2009-05-04T13:39:28.947 回答