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


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

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

но ведь это только 1 пункт внутри.

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

как List<T>

13 332

13 ответов:

вы не можете, потому что 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" });

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

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

когда вы делаете такие операции, как .Список().Добавить() вы создаете новый 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

Я постоянно забываю, что добавить это не типичный метод расширения на 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;
}

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

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

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

"альтернатива" у вас есть.

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

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

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

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

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

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

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

в .net Core есть метод Enumerable.Append Что делает именно это.

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

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

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

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

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

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

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

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

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

конечно, вы можете (я оставляю свой Т-бизнесс):

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

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