Есть ли метод Linq для добавления одного элемента в IEnumerable <T>? - PullRequest
50 голосов
/ 03 февраля 2011

В основном я пытаюсь сделать что-то вроде этого:

image.Layers

, который возвращает IEnumerable для всех слоев, кроме слоя Parent, но в некоторых случаях я просто хочу сделать:

image.Layers.With(image.ParentLayer);

потому что он используется только в нескольких местах по сравнению с сотнями обычного использования, которое удовлетворяется image.Layers. Вот почему я не хочу создавать другое свойство, которое также возвращает слой Parent.

Ответы [ 11 ]

49 голосов
/ 03 февраля 2011

Один из способов состоит в создании одноэлементной последовательности из элемента (например, массива), а затем Concat на оригинал:

image.Layers.Concat(new[] { image.ParentLayer } )

Если вы действительно делаете эточасто стоит написать Append (или аналогичный) метод расширения, такой как , перечисленный здесь , который позволит вам:

image.Layers.Append(image.ParentLayer)
26 голосов
/ 10 июня 2013

Многие реализации уже были даны. Мой выглядит немного по-другому (но работает так же хорошо)

Кроме того, я считаю, что практично также контролировать ЗАКАЗ. таким образом, часто у меня также есть метод ConcatTo, помещающий новый элемент перед.

public static class Utility
{
    /// <summary>
    /// Adds the specified element at the end of the IEnummerable.
    /// </summary>
    /// <typeparam name="T">The type of elements the IEnumerable contans.</typeparam>
    /// <param name="target">The target.</param>
    /// <param name="item">The item to be concatenated.</param>
    /// <returns>An IEnumerable, enumerating first the items in the existing enumerable</returns>
    public static IEnumerable<T> ConcatItem<T>(this IEnumerable<T> target, T item)
    {
        if (null == target) throw new ArgumentException(nameof(target));
        foreach (T t in target) yield return t;
        yield return item;
    }

    /// <summary>
    /// Inserts the specified element at the start of the IEnumerable.
    /// </summary>
    /// <typeparam name="T">The type of elements the IEnumerable contans.</typeparam>
    /// <param name="target">The IEnummerable.</param>
    /// <param name="item">The item to be concatenated.</param>
    /// <returns>An IEnumerable, enumerating first the target elements, and then the new element.</returns>
    public static IEnumerable<T> ConcatTo<T>(this IEnumerable<T> target, T item)
    {
        if (null == target) throw new ArgumentException(nameof(target));
        yield return item;
        foreach (T t in target) yield return t;
    }
}

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

public static class Utility
{
    /// <summary>
    /// Adds the specified element at the end of the IEnummerable.
    /// </summary>
    /// <typeparam name="T">The type of elements the IEnumerable contans.</typeparam>
    /// <param name="target">The target.</param>
    /// <param name="items">The items to be concatenated.</param>
    /// <returns>An IEnumerable, enumerating first the items in the existing enumerable</returns>
    public static IEnumerable<T> ConcatItems<T>(this IEnumerable<T> target, params T[] items) =>
        (target ?? throw new ArgumentException(nameof(target))).Concat(items);

    /// <summary>
    /// Inserts the specified element at the start of the IEnumerable.
    /// </summary>
    /// <typeparam name="T">The type of elements the IEnumerable contans.</typeparam>
    /// <param name="target">The IEnummerable.</param>
    /// <param name="items">The items to be concatenated.</param>
    /// <returns>An IEnumerable, enumerating first the target elements, and then the new elements.</returns>
    public static IEnumerable<T> ConcatTo<T>(this IEnumerable<T> target, params T[] items) =>
        items.Concat(target ?? throw new ArgumentException(nameof(target)));
12 голосов
/ 03 февраля 2011

Нет единого метода, который бы это делал. Наиболее близким является метод Enumerable.Concat, но он пытается объединить IEnumerable<T> с другим IEnumerable<T>. Вы можете использовать следующее, чтобы заставить его работать с одним элементом

image.Layers.Concat(new [] { image.ParentLayer });

Или просто добавьте новый метод расширения

public static IEnumerable<T> ConcatSingle<T>(this IEnumerable<T> enumerable, T value) {
  return enumerable.Concat(new [] { value });
}
7 голосов
/ 16 ноября 2017

Append и Prepend теперь добавлены в .NET Standard Framework, поэтому вам больше не нужно писать свои собственные.Просто сделайте это:

image.Layers.Append(image.ParentLayer)

См. Какие 43 API есть в .Net Standard 2.0, но не в .Net Framework 4.6.1? для большого списка новых функций.

7 голосов
/ 03 февраля 2011

Вы можете использовать Enumerable.Concat :

var allLayers = image.Layers.Concat(new[] {image.ParentLayer});
5 голосов
/ 07 октября 2011

Однажды я сделал для этого симпатичную маленькую функцию:

public static class CoreUtil
{    
    public static IEnumerable<T> AsEnumerable<T>(params T[] items)
    {
        return items;
    }
}

Теперь это возможно:

image.Layers.Append(CoreUtil.AsEnumerable(image.ParentLayer, image.AnotherLayer))
5 голосов
/ 03 февраля 2011

Вы можете сделать что-то вроде:

image.Layers.Concat(new[] { image.ParentLayer });

, который объединяет перечисление с одноэлементным массивом, содержащим то, что вы хотите добавить

3 голосов
/ 07 сентября 2015

Я использую следующие методы расширения, чтобы избежать создания бесполезных Array:

public static IEnumerable<T> ConcatSingle<T>(this IEnumerable<T> enumerable, T value) {
   return enumerable.Concat(value.Yield());
}

public static IEnumerable<T> Yield<T>(this T item) {
    yield return item;
}
3 голосов
/ 03 февраля 2011

Если вам нравится синтаксис .With, напишите его как метод расширения. IEnumerable не заметит другого.

1 голос
/ 03 февраля 2011

Существует метод Concat , который объединяет две последовательности.

...