web-dev-qa-db-ja.com

IEnumerable <T>コレクションに項目を追加する方法

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

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

しかし、やはり内部には1つのアイテムしかありません。

items.Add(item)のようなメソッドはありますか?

List<T>のように

352
ldsenow

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

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

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

しかし、あなたができることは、 new IEnumerableオブジェクト(未指定の型)を作成することです。これは、列挙されると、古いものすべてとあなた自身のものをすべて提供します。そのためにはEnumerable.Concatを使用します。

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

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

427
Pavel Minaev

IEnumerable<T>型はそのような操作をサポートしません。 IEnumerable<T>インターフェースの目的は、消費者がコレクションの内容を閲覧できるようにすることです。値を変更しないでください。

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

あなたができることは、追加された値で新しいIEnumerable<T>を作成するためにAdd extensionメソッドを使うことです。

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;
}
84
JaredPar

代わりに ICollection <T> または IList <T> インターフェイスを使用することを検討しましたが、それらはIEnumerable <T>で 'Add'メソッドを使用したいという非常に理由で存在します。 IEnumerable <T>は、実際の基礎となるオブジェクトが項目の追加/削除をサポートしているかどうかを必ずしも保証することなく、型を列挙型または一連の項目として 'マーク'するために使用されます。また、これらのインターフェースはIEnumerable <T>を実装しているので、IEnumerable <T>で得られるすべての拡張メソッドを取得できます。

48
Abhijeet Patel

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にはありません。

29
Will

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

あなたは「代替」です:

var List = new List(items);
List.Add(otherItem);
16
Paul van Brenk

2番目のメッセージを追加するにはあなたがする必要があります -

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

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

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

9
JanDotNet

あなたが言っているような項目を追加することができないだけでなく、既存の列挙子を持つList<T>(またはほとんどすべての非読み取り専用コレクション)に項目を追加すると、列挙子は無効にされます(InvalidOperationException あれから)。

何らかの種類のデータクエリの結果を集計する場合は、Concat拡張メソッドを使用できます。

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

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

他の人たちは、なぜあなたがIEnumerableにアイテムを追加することができない(そしてそうすべきではない!)のかについて、すでに素晴らしい説明をしています。コレクションを表すインターフェースへのコーディングを続け、addメソッドが必要な場合は、 ICollection または IList にコーディングする必要があります。追加の大当たりとして、これらのインターフェースはIEnumerableを実装しています。

4
jason

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

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

それでも、この関数は入力シーケンスを変更しないことに注意してください。与えられたシーケンスと追加された項目をまとめたラッパーを返すだけです。

4
CXuesong

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

//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;
1
Lrodriguez84

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

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
Juha Sinkkonen