Как добавить элемент в коллекцию IEnumerable <T>?

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

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

но в конце концов он содержит только 1 элемент.

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

как List<T>

Ответ 1

Вы не можете, потому что 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". Кроме того, этот новый объект будет отслеживать изменения в массиве (т.е. всякий раз, когда вы его перечисляете, вы увидите текущие значения элементов).

Ответ 2

Тип 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>, потому что это один из первых, который я в конечном итоге определяю. Здесь

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

Ответ 3

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

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

Ответ 4

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

Ответ 5

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

Вы "альтернатива" это:

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

Ответ 6

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

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

Ответ 7

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

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

Ответ 8

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

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

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

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

Ответ 9

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

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

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

Ответ 10

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

Ответ 11

вы можете это сделать.

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

Ответ 12

Самый простой способ сделать это - просто

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

Ответ 13

Конечно, вы можете (я оставляю ваш T-бизнес в стороне):

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

    return list.Select(i => i);
}