アイテムの範囲をIList変数に追加する方法


81

AddRange()方法はありませんIList<T>

アイテムをIList<T>繰り返し処理したり、Add()メソッドを使用したりせずに、アイテムのリストをに追加するにはどうすればよいですか?

回答:


66

AddRangeList<T>インターフェイスではなく、で定義されます。

にアクセスするために、変数をのList<T>代わりに宣言するか、変数をIList<T>キャストList<T>することができますAddRange

((List<myType>)myIList).AddRange(anotherList);

はそうではないIList<T>かもしれないので、これは良い習慣ではありません(以下のコメントを参照)List<T>が、インターフェースを実装し、AddRangeメソッドを持たない可能性がある他のタイプ-そのような場合、コードがスローするときだけわかります実行時の例外。

したがって、タイプが実際にaであることが確実にわかっている場合を除いて、List<T>を使用しようとしないでくださいAddRange

これを行う1つの方法は、isまたはas演算子を使用して型をテストすることです(C#7以降)。

if(myIList is List<T>)
{
   // can cast and AddRange
}
else
{
   // iterate with Add
}

2
@ mohsen.d-型が生成された場合、生成されたコードを変更する必要はありません(上書きされる可能性があるため)。Concat@Self_Taught_Programmerが回答したように、キャストするかLINQを使用します。
2012年

2
@ mohsen.d-それがあなたのコードである場合、タイプを次のように宣言することもできますList<T>(または、これが適切でない場合AddRangeは、ローカライズされた状態に保つ必要がある場所でキャストを行います-これは非常に低コストの操作です)。
2012年

53
ダメダメダメ。これがそもそもIList <T>である理由は、List <T>の実装以外のものである可能性があるためです。AddRangeメソッドが本当に必要な場合は、BlackjacketMackに示されている拡張メソッドを記述します。
デレクグリア2015年

6
なぜこれが非常に多くの賛成票を受け取ったのかわかりません。それはInvalidCastExceptionList<T>(たとえば、配列)以外で使用された場合のようなものを明らかにスローするからです。
bashis 2016年

3
しかし、キャストは良い考えではありません。パフォーマンスのオーバーヘッドにつながる可能性があります。
Gul Ershad 2017年

79

Listのc#ソースコードを見ると、List.AddRange()には、単純なループでは対処できない最適化があると思います。したがって、拡張メソッドは、IListがリストであるかどうかを確認するだけでよく、リストである場合は、ネイティブのAddRange()を使用する必要があります。

ソースコードをざっと見てみると、.NETの人々は.ToList()などの独自のLinq拡張機能で同様のことを行っています(リストの場合はキャストします...それ以外の場合は作成します)。

public static class IListExtension
{
    public static void AddRange<T>(this IList<T> list, IEnumerable<T> items)
    {
        if (list == null) throw new ArgumentNullException(nameof(list));
        if (items == null) throw new ArgumentNullException(nameof(items));

        if (list is List<T> asList)
        {
            asList.AddRange(items);
        }
        else
        {
            foreach (var item in items)
            {
                list.Add(item);
            }
        }
    }
}

6
最適化の観点から、ここでは実際listList<T>2回キャストしています。そのうちの1つは、asキーワードを使用して最適化できます。
bashis 2016年

@bashisに電話してください。私は常に、新しい変数のダブルキャストとGCのクリーンアップのコストを行ったり来たりしています。しかし、実際にvar listCasted = list as List <T>;を実行できます。if(listCasted!= null)...多分c#6宣言式はこのパターンを変更します:if(myVar.As(out myVarCasted))myVarCasted ...)
BlackjacketMack

上記の最適化でコードを更新できますか?私はまだ最新の機能に精通していません。@BlackjacketMack

2
@ zuckerthoben-両方のアプローチを使用して100万回の反復を繰り返すテストを実行したところ、パフォーマンスに違いはありませんでした。だから私はそれを最適化とは呼びません...さらに、それはコードの行を追加します(しかし、parensのキャストを減らします)。とにかく、私はおそらく最近は 'as'を使用するでしょう:var listCasted = list as List <T>; if(listCasted!= null){listCasted.AddRange(items);}。私見の答えを更新する価値はありませんが、構文上の代替手段として紹介するのは良いことです。
BlackjacketMack

2
今のところあなたはできるif (list is List<T> castedList) { castedList.AddRange(items); }
アンドレマンタス

23

あなたはこのようなことをすることができます:

IList<string> oIList1 = new List<string>{"1","2","3"};
IList<string> oIList2 = new List<string>{"4","5","6"};
IList<string> oIList3 = oIList1.Concat(oIList2).ToList();

したがって、基本的にはConcat()拡張機能を使用ToList()して、と同様の機能を取得しAddRange()ます。

ソース


1
あなたのアプローチの問題は、Enumerable.Concatによって実装されSystem.Linq.Enumerable、そのメソッドの戻り値がIEnumerable<TSource>であるため、キャストバックすべきではないとIList<TSource>思います-ソースコードをチェックせずに実装の詳細がわからないため、何か他のものを返す可能性があります-ただし、変更されないという保証はありません。したがって、複数の.NETバージョンをサポートする場合は、特別な注意を払う必要があります。
jweyrich 2015

8

次のような拡張メソッドを作成することもできます。

internal static class EnumerableHelpers
{
    public static void AddRange<T>(this IList<T> collection, IEnumerable<T> items)
    {
        foreach (var item in items)
        {
            collection.Add(item);
        }
    }
}

使用法:

        IList<int> collection = new int[10]; //Or any other IList
        var items = new[] {1, 4, 5, 6, 7};
        collection.AddRange(items);

これはまだアイテムを反復処理していますが、呼び出すたびに反復を記述したりキャストしたりする必要はありません。


2

LINQを使用した別の答えは、追加するものがaであるList<T>ToList()、それを呼び出すことができる場合です。

IEnumerable<string> toAdd = new string[] {"a", "b", "c"};
IList<string> target = new List<string>();

toAdd.ToList().ForEach(target.Add);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.