AddRangeを使用してサブクラスのコレクションを追加する
-
03-07-2019 - |
質問
これら2つのクラスがある場合:
class A {}
class B : A {}
そして私はリストを作成します<!> lt; A <!> gt;リスト<!> lt; B <!> gt;を追加したいList <!> lt; A <!> gt; .AddRange(List <!> lt; B <!> gt;)を呼び出しますが、コンパイラは拒否します:
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;});
しかし、それは不必要に複雑で、私は変換していないので、間違って名付けています。私はキャストしています。
質問:リストのような独自のコレクションを記述する場合、BのコレクションをAのコレクションにワンライナーとしてコピーできるメソッドを追加します。リストに似て! (最大で、引数はコレクションと型の継承チェックの両方であることを意味します。)
解決
実際、ジェネリック型は現在のところバリアントではありません。 C#4.0では、IEnumerable <B
<!> gt; IEnumerable <A
<!> gtに変換可能になります。 Bが参照変換を介してAに変換できる場合。この機能の設計の詳細については、次を参照してください。
他のヒント
.netのジェネリックは(まだ)共分散をサポートしていないため、これは残念ながら機能しません。
この問題を克服するために、小さなヘルパーメソッドまたはクラスを作成できます。
独自のリストクラスを実装する場合、追加のジェネリックパラメーターを使用して共分散を追加できます。
class MyList<T> {
void AddRange<U>(IEnumerable<U> items) where U: T {
foreach (U item in items) {
Add(item);
}
}
}
あなただけではできません:
listOfAItems.AddRange(listOfBItems.Cast<A>());
LINQを使用してこれを達成できました...
listOfAItems.AddRange(listOfBItems.Cast<A>());
ジェネリック型がバリアントではない状況に陥った場合、次の拡張メソッドを使用すると作業が楽になります。
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);
私が思いつくことができるのはこれだけです
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;)を実行することを意味します。引数が列挙可能でない場合、または型の継承がうまくいかないため、私の質問の最大の型安全性要件を満たす場合、これは失敗します。