Мой вопрос как название выше. Например,
IEnumerable<T> items = new T[]{new T("msg")};
items.ToList().Add(new T("msg2"));
но в конце концов он содержит только 1 элемент.
Можем ли мы иметь такой метод, как items.Add(item)
?
как List<T>
Мой вопрос как название выше. Например,
IEnumerable<T> items = new T[]{new T("msg")};
items.ToList().Add(new T("msg2"));
но в конце концов он содержит только 1 элемент.
Можем ли мы иметь такой метод, как items.Add(item)
?
как List<T>
Вы не можете, потому что 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". Кроме того, этот новый объект будет отслеживать изменения в массиве (т.е. всякий раз, когда вы его перечисляете, вы увидите текущие значения элементов).
Тип 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;
}
Рассматривали ли вы ICollection<T>
использование ICollection<T>
или IList<T>
, они существуют по той самой причине, что вы хотите иметь метод Add
для IEnumerable<T>
.
IEnumerable<T>
используется, чтобы "пометить" тип как... ну, перечисляемый или просто последовательность элементов, не обязательно делая какие-либо гарантии того, поддерживает ли реальный базовый объект добавление/удаление элементов. Также помните, что эти интерфейсы реализуют IEnumerable<T>
поэтому вы получаете все методы расширений, которые вы получаете с IEnumerable<T>
.
Несколько коротких, сладких методов расширения на 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 не поддерживает добавление элементов к нему.
Вы "альтернатива" это:
var List = new List(items);
List.Add(otherItem);
В .net Core есть метод Enumerable.Append
который делает именно это.
Исходный код метода доступен на GitHub.... Реализация (более сложная, чем предложения в других ответах) стоит посмотреть :).
Чтобы добавить второе сообщение, которое вам нужно -
IEnumerable<T> items = new T[]{new T("msg")};
items = items.Concat(new[] {new T("msg2")})
Нельзя добавлять не такие элементы, как вы заявляете, но если вы добавите элемент в List<T>
(или почти любую другую коллекцию, не предназначенную для чтения), в которой у вас есть существующий перечислитель, перечислитель недействителен ( затем бросает InvalidOperationException
).
Если вы агрегируете результаты из определенного типа запросов данных, вы можете использовать метод расширения Concat
:
Изменить: я изначально использовал расширение Union
в примере, что не совсем правильно. Мое приложение широко использует его, чтобы убедиться, что перекрывающиеся запросы не дублируют результаты.
IEnumerable<T> itemsA = ...;
IEnumerable<T> itemsB = ...;
IEnumerable<T> itemsC = ...;
return itemsA.Concat(itemsB).Concat(itemsC);
Я просто пришел сюда, чтобы сказать, что, помимо метода расширения Enumerable.Concat
, в .NET Core 1.1.1 существует другой метод с именем Enumerable.Append
. Последний позволяет объединить один элемент в существующую последовательность. Поэтому ответ Aamol также можно записать как
IEnumerable<T> items = new T[]{new T("msg")};
items = items.Append(new T("msg2"));
Тем не менее, обратите внимание, что эта функция не изменит входную последовательность, она просто вернет оболочку, которая помещает данную последовательность и добавленный элемент вместе.
Другие уже дали большие объяснения относительно того, почему вы не можете (и не должны!) иметь возможность добавлять элементы в IEnumerable
. Я только добавлю, что если вы хотите продолжить кодирование интерфейса, представляющего коллекцию, и хотите добавить метод, вы должны ввести код ICollection
или IList
. В качестве добавленного достоинства эти интерфейсы реализуют IEnumerable
.
вы можете это сделать.
//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
Конечно, вы можете (я оставляю ваш T-бизнес в стороне):
public IEnumerable<string> tryAdd(IEnumerable<string> items)
{
List<string> list = items.ToList();
string obj = "";
list.Add(obj);
return list.Select(i => i);
}