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

StackOverflow https://stackoverflow.com/questions/1210295

  •  06-07-2019
  •  | 
  •  

Вопрос

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

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 (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> и добавление значения в этот список.Никакой связи с первоначальным списком он не имеет.

Что вы можете сделать, так это использовать метод 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».

РЕДАКТИРОВАТЬ

Я постоянно забываю, что 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 > или 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")})

Вы можете не только добавлять элементы, которые вы указали, но и добавлять элемент в Список < 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 . , Последнее позволяет объединить один элемент в существующую последовательность. Так что ответ Аамола также можно записать как

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);
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top