代码之家  ›  专栏  ›  技术社区  ›  Colin Burnett

使用addrange添加子类的集合

  •  1
  • Colin Burnett  · 技术社区  · 15 年前

    如果我有这两门课:

    class A {}
    class B : A {}
    

    我列出了一个列表,但我想通过调用list<a>.addrange(list<b>)向其中添加一个列表,但编译器拒绝:

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

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

    我的解决方案是通过列表枚举并单独添加项,因为列表<a>。添加(a项)将与b项一起使用:

    foreach(B item in listOfBItems)
    {
        listOfAItems.Add(item);
    }
    

    然而,这是相当不具表现力的,因为我想要的只是addrange。

    我可以用

    List<B>.ConvertAll<A>(delegate(B item) {return (A)item;});
    

    但这是不必要的复杂和误称,因为我不转换,我是演员。

    问题 :如果我要编写自己的类似列表的集合,我会在其中添加什么方法,使我能够将B的集合作为类似于列表<A>.addrange(列表<B>)的一行程序复制到A的集合中? 保持最大类型安全。(最大限度地说,我的意思是,这个参数既是一个集合,也是一个类型的内在检查。)

    6 回复  |  直到 8 年前
        1
  •  8
  •   Eric Lippert    15 年前

    实际上,泛型类型现在不是变量。在C 4.0中,IEnumerable <B >将转换为IEnumerable <A gt; 如果B通过参考转换转换为A . 有关此功能设计的一些详细信息,请参阅:

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

        2
  •  1
  •   Lucero    15 年前

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

    但是,您可以创建一个小的助手方法或类来解决这个问题。

    如果实现自己的列表类,则可以使用其他泛型参数添加协方差:

    class MyList<T> {
        void AddRange<U>(IEnumerable<U> items) where U: T {
            foreach (U item in items) {
                Add(item);
            }
        }
    }
    
        3
  •  1
  •   BFree    15 年前

    你不能这样做吗?

    listOfAItems.AddRange(listOfBItems.Cast<A>());
    
        4
  •  1
  •   Ian Newland    11 年前

    我可以用LINQ来实现这一点…

    listOfAItems.AddRange(listOfBItems.Cast<A>());
    
        5
  •  1
  •   Mr Lister hawi    8 年前

    如果您发现自己处于一种通用类型不是变量的情况下,下面的扩展方法可以使您的生活更轻松:

    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);
    
        6
  •  0
  •   Colin Burnett    15 年前

    我唯一能想到的就是这个

    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>)。如果参数不可枚举,或者类型继承不起作用,那么它将失败,从而满足我问题的最大类型安全要求。