IEnumerable <T>コレクションにアイテムを追加するにはどうすればよいですか?


405

上記のタイトルとして私の質問。例えば、

IEnumerable<T> items = new T[]{new T("msg")};
items.ToList().Add(new T("msg2"));

結局のところ、中には1つのアイテムしかありません。

次のような方法はありitems.Add(item)ますか?

以下のような List<T>


4
IEnumerable<T>コレクションのクエリのみを目的としています。これは、LINQフレームワークのバックボーンです。それはいつものようないくつかの他のコレクションの抽象化であるCollection<T>List<T>または、Array。インターフェイスは、拡張するコレクションを一度に1項目ずつウォークできるようにGetEnumeratorするのインスタンスを返すメソッドのみを提供しますIEnumerator<T>。LINQ拡張メソッドは、このインターフェイスによって制限されます。IEnumerable<T>複数のコレクションの一部の集約を表す場合があるため、読み取り専用に設計されています。
ジョーダン

3
この例では、単に宣言IList<T>の代わりに、IEnumerable<T>IList<T> items = new T[]{new T("msg")}; items.Add(new T("msg2"));
NightOwl888

回答:


480

は、IEnumerable<T>必ずしもアイテムを追加できるコレクションを表すとは限らないため、できません。実際、コレクションを表すとは限りません。例えば:

IEnumerable<string> ReadLines()
{
     string s;
     do
     {
          s = Console.ReadLine();
          yield return s;
     } while (!string.IsNullOrEmpty(s));
}

IEnumerable<string> lines = ReadLines();
lines.Add("foo") // so what is this supposed to do??

ただし、できることは、(指定されていないタイプの)新しい IEnumerableオブジェクトを作成することです。列挙すると、古いオブジェクトのすべてのアイテムに加えて、いくつかの独自のアイテムが提供されます。あなたEnumerable.Concatはそのために使用します:

 items = items.Concat(new[] { "foo" });

これは配列オブジェクトを変更しません(とにかく項目を配列に挿入することはできません)。ただし、配列内のすべての項目をリストしてから「Foo」を含む新しいオブジェクトを作成します。さらに、その新しいオブジェクトは配列の変更を追跡します(つまり、それを列挙するたびに、アイテムの現在の値が表示されます)。


3
単一の値を追加する配列を作成すると、オーバーヘッドが少し高くなります。単一の値Concatの拡張メソッドを定義すると、もう少し再利用できます。
JaredPar 2009

4
状況によって異なりますが、価値があるかもしれませんがConcat、ループで繰り返し呼び出されない限り、時期尚早の最適化と呼びたくなります。そうである場合は、とにかく大きな問題があります。それは、毎回Concat、列挙子の層が1つ増えるためです。つまり、その終わりまでに、1回の呼び出しMoveNextで、反復数と同じ長さの呼び出しのチェーンが生成されます。
Pavel Minaev 2009

2
@パベル、へへ。通常、気になるのは、配列を作成することによるメモリのオーバーヘッドではありません。それは私が迷惑だと思う追加のタイピングです:)。
JaredPar 2009

2
そのため、IEnumerableが何をするかをもう少し理解します。変更されることを意図したものではなく、ビューである必要があります。皆さんありがとう
ldsenow 2009

7
次のようにIEnumerable<T>オーバーロードoperator+を含む、C#の構文シュガーのConnect機能リクエストがありましたConcat(ところで、VBではIEnumerable配列のようにインデックスを作成できることを知っていますか- 内部で使用ElementAtしています):connect.microsoft.com / VisualStudio / feedback /…Concat左と右の両方で1つのアイテムを取得するためにさらに2つのオーバーロードを取得する場合、これによりタイピングが削減されますxs +=x
。Mads(Torgersen

98

タイプIEnumerable<T>はそのような操作をサポートしていません。IEnumerable<T>インターフェースの目的は、コンシューマーがコレクションのコンテンツを表示できるようにすることです。値を変更しません。

.ToList()。Add()などの操作を実行すると、新しいList<T>リストが作成され、そのリストに値が追加されます。元のリストとは関係ありません。

あなたができることは、拡張機能の追加メソッドを使用しIEnumerable<T>て、追加された値で新しいものを作成することです。

items = items.Add("msg2");

この場合でも、元のIEnumerable<T>オブジェクトは変更されません。これは、参照を保持することで確認できます。例えば

var items = new string[]{"foo"};
var temp = items;
items = items.Add("bar");

この一連の操作の後も、変数tempは値のセット内の単一の要素「foo」を持つ列挙型のみを参照しますが、項目は値「foo」と「bar」を持つ別の列挙型を参照します。

編集

AddはIEnumerable<T>、私が定義する最初のメソッドの1つであるため、通常の拡張メソッドではないことを常に忘れています。ここにあります

public static IEnumerable<T> Add<T>(this IEnumerable<T> e, T value) {
  foreach ( var cur in e) {
    yield return cur;
  }
  yield return value;
}

12
それはConcat:) と呼ばれます
Pavel Minaev 2009

3
@Pavel、指摘してくれてありがとう。別のIEnumerable <T>が必要なため、Concatでも機能しません。プロジェクトで定義する一般的な拡張メソッドを貼り付けました。
JaredPar 2009

10
Addただし、Add(コレクションだけでなく)事実上他の.NETタイプでは、コレクションがインプレースで変更されるため、私はそれを呼び出さないでしょう。たぶんWith?または、それは単なるの別の過負荷である可能性もありますConcat
Pavel Minaev 2009

4
通常は可変性を暗示するConcatので、オーバーロードがおそらくより良い選択であることに同意しAddます。
Dan Abramov、2011

15
ボディを変更するのはどうreturn e.Concat(Enumerable.Repeat(value,1));ですか?
Roy Tinker

58

代わりにICollection<T>またはの使用を検討したことがありますがIList<T>、それらはにAddメソッドを置きたいというまさにその理由のために存在しますIEnumerable<T>

IEnumerable<T>実際の基本オブジェクトがアイテムの追加/削除をサポートするかどうかを必ずしも保証することなく、タイプを「...」、「列挙可能」、または単にアイテムのシーケンスとして「マーク」するために使用されます。また、これらのインターフェースが実装するIEnumerable<T>ので、取得したすべての拡張メソッドも取得できることを忘れないでくださいIEnumerable<T>


31

いくつかの短い、甘い拡張メソッドをオンIEnumerableIEnumerable<T>して私のためにそれを行います:

public static IEnumerable Append(this IEnumerable first, params object[] second)
{
    return first.OfType<object>().Concat(second);
}
public static IEnumerable<T> Append<T>(this IEnumerable<T> first, params T[] second)
{
    return first.Concat(second);
}   
public static IEnumerable Prepend(this IEnumerable first, params object[] second)
{
    return second.Concat(first.OfType<object>());
}
public static IEnumerable<T> Prepend<T>(this IEnumerable<T> first, params T[] second)
{
    return second.Concat(first);
}

エレガント(まあ、非ジェネリックバージョンを除く)。これらのメソッドはBCLにはありません。


最初のPrependメソッドはエラーを出します。「 'object []'には 'Concat'の定義が含まれておらず、最適な拡張メソッドオーバーロード 'System.Linq.Enumerable.Concat <TSource>(System.Collections.Generic.IEnumerable <TSource>、System.Collections.Generic。 IEnumerable <TSource>) 'に無効な引数があります "
Tim Newton

@TimNewtonあなたはそれを間違っています。エラーコードのスニペットからは誰にもわからないので、誰が理由を知っていますか。Protip:ToString()エラーの詳細をキャプチャするため、常に例外をキャッチして実行します。include System.Linq;あなたはファイルの先頭にいないと思いますが、誰が知っていますか?自分で考えてみて、できない場合は同じエラーを示す最小限のプロトタイプを作成し、質問に助けを求めてください。

上記のコードをコピーして貼り付けました。私が言及したエラーを出しているプリペンドを除いて、それらはすべて問題なく動作します。ランタイム例外ではなく、コンパイル時例外です。
Tim Newton

@TimNewton:私はあなたがそれが完全に機能していることについてあなたが話していることを知りませんあなたは明らかにあなたが編集の変更を無視して間違いです私は今私の道にいるに違いありません、先生、

多分それは私たちの環境について何か違うものです。VS2012と.NET 4.5を使用しています。これにもう時間を無駄にしないでください。小さなコードではあるが、上のコードに問題があることを指摘したいと思いました。とにかく、私はこの答えが役に立つと賛成しました。ありがとう。
Tim Newton

25

.net Coreには、Enumerable.Appendまさにそれを行うメソッドがあります。

メソッドのソースコードはGitHub入手できます。....(他の回答の提案よりも洗練された)実装は一見の価値があります:)。


3
:これは、だけでなく、コアでも、フレームワーク4.7.1以降であるdocs.microsoft.com/en-us/dotnet/api/...
sschoof

:プリペンドも存在することに注意してくださいdocs.microsoft.com/en-us/dotnet/api/...
codidact.comに移動aloisdg

22

IEnumerableはアイテムの追加をサポートしていません。

あなたの「代替」は:

var List = new List(items);
List.Add(otherItem);

4
あなたの提案が唯一の選択肢ではありません。たとえば、ICollectionとIListはどちらもここでは妥当なオプションです。
ジェイソン

6
しかし、インスタンス化することもできません。
ポールファンブレンク

16

2番目のメッセージを追加するには、次のことを行う必要があります-

IEnumerable<T> items = new T[]{new T("msg")};
items = items.Concat(new[] {new T("msg2")})

1
ただし、Concatメソッドの結果をitemsオブジェクトに割り当てる必要もあります。
Tomasz Przychodzki 2017

1
はい、Tomaszのとおりです。最後の式を変更して、連結された値を項目に割り当てました。
Aamol 2017

8

状態のように項目を追加できないだけでなくList<T>、既存の列挙子がある(またはその他のほとんどの非読み取り専用コレクション)に項目を追加すると、列挙子は無効になります(InvalidOperationExceptionそれ以降はスローされます)。

あるタイプのデータクエリの結果を集計する場合は、Concat拡張メソッドを使用できます。

編集:私はもともとUnionこの例で拡張機能を使用しましたが、これは実際には正しくありません。私のアプリケーションはこれを広範囲に使用して、重複するクエリが結果を複製しないようにします。

IEnumerable<T> itemsA = ...;
IEnumerable<T> itemsB = ...;
IEnumerable<T> itemsC = ...;
return itemsA.Concat(itemsB).Concat(itemsC);

8

Enumerable.Concat拡張メソッド以外にもEnumerable.Append、.NET Core 1.1.1で名前が付けられたメソッドがあるようです。後者では、単一のアイテムを既存のシーケンスに連結できます。したがって、Aamolの答えは次のようにも書くことができます。

IEnumerable<T> items = new T[]{new T("msg")};
items = items.Append(new T("msg2"));

ただし、この関数は入力シーケンスを変更せず、指定されたシーケンスと追加されたアイテムをまとめたラッパーを返すだけであることに注意してください。


4

他のユーザーは、アイテムをに追加できない(できないはずの)理由について、すでにすばらしい説明をしていますIEnumerable。コレクションを表すインターフェイスへのコーディングを継続していて、addメソッドが必要な場合は、ICollectionまたはにコーディングする必要があることだけを追加しますIList。追加の大当たりとして、これらのインターフェースはを実装していIEnumerableます。


1

あなたはこれを行うことができます。

//Create IEnumerable    
IEnumerable<T> items = new T[]{new T("msg")};

//Convert to list.
List<T> list = items.ToList();

//Add new item to list.
list.add(new T("msg2"));

//Cast list to IEnumerable
items = (IEnumerable<T>)items;

8
この質問は5年前に、より完全な形で回答されました。質問に対する適切な回答の例として、受け入れられた回答を見てください。
pquest、2014年

1
最後の行は:items =(IEnumerable <T>)list;
ベレンマーティン

0

それを行う最も簡単な方法は単純です

IEnumerable<T> items = new T[]{new T("msg")};
List<string> itemsList = new List<string>();
itemsList.AddRange(items.Select(y => y.ToString()));
itemsList.Add("msg2");

次に、IEnumerableインターフェイスを実装しているため、リストをIEnumerableとして返すこともできます。


0

あなたのようにあなたのオブジェクトを作成する場合IEnumerable<int> Ones = new List<int>(); 次に、あなたが行うことができます((List<int>)Ones).Add(1);


-8

確かに、あなたはできます(私はあなたのTビジネスを脇に置きます):

public IEnumerable<string> tryAdd(IEnumerable<string> items)
{
    List<string> list = items.ToList();
    string obj = "";
    list.Add(obj);

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