Как добавить элемент в коллекцию IEnumerable?
мой вопрос в заголовке выше. Например,
IEnumerable<T> items = new T[]{new T("msg")};
items.ToList().Add(new T("msg2"));
но ведь это только 1 пункт внутри.
можем ли мы иметь такой метод, как items.Add(item)
?
как List<T>
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