ДобавитьRange в коллекцию

Сотрудник спросил меня, как добавить диапазон в коллекцию. У него есть класс, который наследуется от Collection<T>. Там свойство get-only этого типа, которое уже содержит некоторые элементы. Он хочет добавить элементы в другую коллекцию к коллекции свойств. Как он может сделать это в стиле С# 3? (Обратите внимание на ограничение свойства get-only, которое предотвращает такие решения, как объединение и переназначение.)

Конечно, foreach с Property. Добавить будет работать. Но a List<T> -style AddRange был бы намного более изящным.

Достаточно легко написать метод расширения:

public static class CollectionHelpers
{
    public static void AddRange<T>(this ICollection<T> destination,
                                   IEnumerable<T> source)
    {
        foreach (T item in source)
        {
            destination.Add(item);
        }
    }
}

Но у меня такое чувство, что я изобретаю колесо. Я не нашел ничего подобного в System.Linq или morelinq.

Плохой дизайн? Просто позвоните Добавить? Отсутствует очевидное?

Ответ 1

Нет, это кажется вполне разумным. Существует метод List<T>.

Ответ 2

Попробуйте выполнить листинг в List в методе расширения до запуска цикла. Таким образом, вы можете воспользоваться преимуществами List.AddRange.

public static void AddRange<T>(this ICollection<T> destination,
                               IEnumerable<T> source)
{
    List<T> list = destination as List<T>;

    if (list != null)
    {
        list.AddRange(source);
    }
    else
    {
        foreach (T item in source)
        {
            destination.Add(item);
        }
    }
}

Ответ 3

Так как .NET4.5, если вы хотите, чтобы один лайнер вы можно использовать System.Collections.Generic ForEach.

source.ForEach(o => destination.Add(o));

или даже короче, чем

source.ForEach(destination.Add);

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

Также не попробуйте назначить его как

var x = source.ForEach(destination.Add) 

причина ForEach недействительна.

Ответ 4

Помните, что каждый Add будет проверять емкость коллекции и изменять ее по мере необходимости (медленнее). С помощью AddRange в коллекции будет задана емкость, а затем добавлены элементы (быстрее). Этот метод расширения будет очень медленным, но будет работать.

Ответ 5

Классы C5 Generic Collections Library поддерживают метод AddRange. C5 имеет гораздо более надежный интерфейс, который фактически раскрывает все функции его основных реализаций и совместим с интерфейсами System.Collections.Generic ICollection и IList, что означает, что коллекции C5 могут быть легко заменены как базовые реализация.

Ответ 6

Вы можете добавить свой диапазон IEnumerable в список, а затем установить ICollection = в список.

        IEnumerable<T> source;

        List<item> list = new List<item>();
        list.AddRange(source);

        ICollection<item> destination = list;

Ответ 7

Или вы можете просто сделать расширение ICollection следующим образом:

 public static ICollection<T> AddRange<T>(this ICollection<T> @this, IEnumerable<T> items)
    {
        foreach(var item in items)
        {
            @this.Add(item);
        }

        return @this;
    }

Использовать его было бы так же, как использовать его в списке:

collectionA.AddRange(IEnumerable<object> items);

Ответ 8

Вот немного более продвинутая/готовая к производству версия:

    public static class CollectionExtensions
    {
        public static TCol AddRange<TCol, TItem>(this TCol destination, IEnumerable<TItem> source)
            where TCol : ICollection<TItem>
        {
            if(destination == null) throw new ArgumentNullException(nameof(destination));
            if(source == null) throw new ArgumentNullException(nameof(source));

            // don't cast to IList to prevent recursion
            if (destination is List<TItem> list)
            {
                list.AddRange(source);
                return destination;
            }

            foreach (var item in source)
            {
                destination.Add(item);
            }

            return destination;
        }
    }