Как я могу добавить элемент в коллекцию IEnumerable <T>? - PullRequest
360 голосов
/ 31 июля 2009

Мой вопрос, как указано выше. Например,

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

но ведь в нем всего 1 предмет.

Можем ли мы иметь метод, подобный items.Add(item)?

как List<T>

Ответы [ 13 ]

433 голосов
/ 31 июля 2009

Вы не можете, потому что 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??

Однако вы можете создать новый IEnumerable объект (неопределенного типа), который при перечислении предоставит все элементы старого, а также некоторые собственные. Для этого вы используете Enumerable.Concat:

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

Этот не изменит объект массива (вы все равно не можете вставлять элементы в массивы). Но он создаст новый объект, который перечислит все элементы в массиве, а затем "Foo". Кроме того, этот новый объект будет отслеживать изменения в массиве (т.е. при каждом его перечислении вы будете видеть текущие значения элементов).

85 голосов
/ 31 июля 2009

Тип IEnumerable<T> не поддерживает такие операции. Назначение интерфейса IEnumerable<T> - позволить потребителю просматривать содержимое коллекции. Не изменять значения.

Когда вы выполняете такие операции, как .ToList (). Add (), вы создаете новый List<T> и добавляете значение в этот список. Не имеет связи с исходным списком.

Что вы можете сделать, это использовать метод Add extension, чтобы создать новый IEnumerable<T> с добавленным значением.

items = items.Add("msg2");

Даже в этом случае он не изменит исходный объект IEnumerable<T>. Это можно проверить, сохранив ссылку на него. Например

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

После этого набора операций переменная temp по-прежнему будет ссылаться только на перечислимое значение с одним элементом "foo" в наборе значений, в то время как элементы будут ссылаться на другое перечисляемое значение со значениями "foo" и "bar".

EDIT

Я постоянно забываю, что Add - это не типичный метод расширения для IEnumerable<T>, потому что это один из первых, которые я в итоге определил. Вот оно

public static IEnumerable<T> Add<T>(this IEnumerable<T> e, T value) {
  foreach ( var cur in e) {
    yield return cur;
  }
  yield return value;
}
49 голосов
/ 31 июля 2009

Рассматривали ли вы использование ICollection<T> или IList<T> интерфейсов вместо этого, они существуют по той самой причине, что вы хотите использовать метод Add для IEnumerable<T> .

IEnumerable<T> используется для того, чтобы «пометить» тип как ... ну, перечисляемый или просто последовательность элементов без обязательной гарантии того, что реальный базовый объект поддерживает добавление / удаление элементов. Также помните, что эти интерфейсы реализуют IEnumerable<T>, поэтому вы получаете все методы расширений, которые вы получаете также с IEnumerable<T>.

29 голосов
/ 10 января 2013

Несколько коротких, приятных методов расширения для IEnumerable и IEnumerable<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.

17 голосов
/ 31 июля 2009

Нет, IEnumerable не поддерживает добавление элементов к нему.

Вы 'альтернатива':

var List = new List(items);
List.Add(otherItem);
11 голосов
/ 12 августа 2017

В .net Core есть метод Enumerable.Append, который делает именно это.

Исходный код метода доступен на GitHub. .... Реализация (более сложная, чем предложения в других ответах) стоит посмотреть :).

11 голосов
/ 28 апреля 2016

Чтобы добавить второе сообщение, вам нужно -

IEnumerable<T> items = new T[]{new T("msg")};
items = items.Concat(new[] {new T("msg2")})
8 голосов
/ 31 июля 2009

Мало того, что вы не можете не добавлять элементы, как вы заявляете, но если вы добавляете элемент в List<T> (или почти в любую другую не предназначенную только для чтения коллекцию), для которой у вас есть существующий перечислитель, перечислитель становится недействительным ( с этого момента InvalidOperationException).

Если вы агрегируете результаты какого-либо запроса данных, вы можете использовать метод расширения Concat:

Edit: я первоначально использовал расширение Union в примере, что не совсем правильно. Мое приложение широко использует его, чтобы убедиться, что перекрывающиеся запросы не дублируют результаты.

IEnumerable<T> itemsA = ...;
IEnumerable<T> itemsB = ...;
IEnumerable<T> itemsC = ...;
return itemsA.Concat(itemsB).Concat(itemsC);
4 голосов
/ 10 мая 2017

Я просто пришел сюда, чтобы сказать, что, кроме метода расширения Enumerable.Concat, в .NET Core 1.1.1, похоже, есть еще один метод с именем Enumerable.Append. Последнее позволяет объединить один элемент в существующую последовательность. Таким образом, ответ Аамола также может быть записан как

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

Тем не менее, обратите внимание, что эта функция не изменит последовательность ввода, она просто вернет оболочку, которая соединит данную последовательность и добавленный элемент вместе.

4 голосов
/ 31 июля 2009

Другие уже дали отличные объяснения относительно того, почему вы не можете (и не должны!) Добавлять элементы в IEnumerable. Я только добавлю, что если вы хотите продолжить кодирование интерфейса, представляющего коллекцию, и хотите добавить метод, вам следует кодировать ICollection или IList. В качестве дополнительного бонуса, эти интерфейсы реализуют IEnumerable.

...