1

これら2つのクラスがある場合:

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<A>.Add(A item) が B 項目で機能するため、 List<B> を列挙して項目を個別に追加することです。

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

ただし、私が欲しいのは AddRange だけなので、それはかなり非表現的です。

私は使用できます

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

しかし、それは不必要に複雑で、間違った呼び方です。

質問: 自分自身のリストのようなコレクションを作成する場合、B のコレクションを A のコレクションに List<A>.AddRange(List <B>)であり、最大限の型安全性を保持します。(そして最大で、引数がコレクションと型の継承チェックの両方であることを意味します。)

4

6 に答える 6

8

実際、ジェネリック型は現時点ではバリアントではありません。C# 4.0 では、B が参照変換によって A に変換可能な場合、IEnumerable >は IEnumerable <B> に変換可能になります。この機能の設計の詳細については、次を参照してください。<A

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

于 2009-05-04T16:09:46.310 に答える
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 に答える
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 に答える
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 に答える