як додати діапазон елементів до змінної IList


81

немає AddRange()методу для IList<T>.

Як я можу додати список елементів до a, IList<T>не перебираючи елементи та використовуючи Add()метод?

Відповіді:


66

AddRangeвизначається на List<T>, а не на інтерфейсі.

Ви можете оголосити змінну List<T>замість того, щоб IList<T>або передати її List<T>, щоб отримати доступ до AddRange.

((List<myType>)myIList).AddRange(anotherList);

Це не є доброю практикою (див. Коментарі нижче), оскільки це IList<T>може бути не a List<T>, але якийсь інший тип, який реалізував інтерфейс і може дуже не мати AddRangeметоду - у такому випадку ви дізнаєтесь лише тоді, коли ваш код викине виняток під час виконання.

Отже, якщо ви точно не знаєте, що тип справді такий, List<T>вам не слід намагатися використовувати AddRange.

Один із способів це зробити - перевірити тип за допомогою операторів is або як (з C # 7).

if(myIList is List<T>)
{
   // can cast and AddRange
}
else
{
   // iterate with Add
}

2
@ mohsen.d - Якщо тип генерується, ви не хочете змінювати згенерований код (оскільки він може бути перезаписаний). Або киньте, або використовуйте LINQ Concat, як відповів @Self_Taught_Programmer .
Почато

2
@ mohsen.d - Якщо це ваш код, ви можете також оголосити тип як List<T>(або, якщо це не вдалий вибір для вас, зробіть акторський склад там, де вам потрібно, щоб AddRangeвін залишався локалізованим - це дуже дешева операція ).
Почато

53
Ні-ні-ні. Причиною того, що для початку це IList <T>, є те, що це може бути щось інше, ніж реалізація List <T>. Напишіть метод розширення, як показано BlackjacketMack, якщо вам дійсно потрібен метод AddRange.
Дерек Грір,

6
Поняття не маю, чому це отримало стільки голосів "за", оскільки це однозначно викине щось на зразок InvalidCastExceptionвикористання, якщо що-небудь, крім List<T>(масиву, наприклад).
баші

3
Але кастинг - це не гарна ідея. Це може призвести до накладних витрат.
Gul Ershad

79

Якщо подивитися на вихідний код c # для List , я думаю, List.AddRange () має оптимізації, які простий цикл не стосується. Отже, метод розширення повинен просто перевірити, чи є IList списком, і якщо так, використовуйте його власний AddRange ().

Оглядаючи вихідний код, ви бачите, що люди .NET роблять подібні речі у своїх власних розширеннях Linq для таких речей, як .ToList () (якщо це список, додайте його ... інакше створіть його).

public static class IListExtension
{
    public static void AddRange<T>(this IList<T> list, IEnumerable<T> items)
    {
        if (list == null) throw new ArgumentNullException(nameof(list));
        if (items == null) throw new ArgumentNullException(nameof(items));

        if (list is List<T> asList)
        {
            asList.AddRange(items);
        }
        else
        {
            foreach (var item in items)
            {
                list.Add(item);
            }
        }
    }
}

6
З точки зору оптимізації, ви на самому справі кастинг listв List<T>два рази тут. Один з яких можна оптимізувати за допомогою asключового слова.
баші

Хороший дзвінок @bashis. Я завжди повертаюся туди-сюди щодо вартості подвійного складання чи очищення GC нашої нової var. Але ми справді могли б зробити var listCasted = list як List <T>; if (listCasted! = null) ... Можливо, вирази декларації c # 6 змінять цей шаблон: if (myVar.As (out myVarCasted)) myVarCasted ...)
BlackjacketMack

Чи можете ви оновити код із зазначеною оптимізацією? Я ще не так вільно володію новітніми функціями. @BlackjacketMack

2
@zuckerthoben - Я щойно провів тест з ітерацією одного мільйона ітерацій, використовуючи обидва підходи, і різниці в продуктивності не було. Тому я б не називав це оптимізацією ... крім того, він додає рядок коду (але зменшує приведення паренсів). Як би там не було, я б, мабуть, сьогодні використовував 'як': var listCasted = list як List <T>; if (listCasted! = null) {listCasted.AddRange (items);}. Не варто оновлювати відповідь IMHO, але добре ввести як синтаксичну альтернативу.
BlackjacketMack

2
Наразі це можна зробитиif (list is List<T> castedList) { castedList.AddRange(items); }
Андре Мантас,

23

Ви можете зробити щось подібне:

IList<string> oIList1 = new List<string>{"1","2","3"};
IList<string> oIList2 = new List<string>{"4","5","6"};
IList<string> oIList3 = oIList1.Concat(oIList2).ToList();

Отже, в основному ви б використовували Concat()розширення та ToList()отримували подібну функціональність, як AddRange().

Джерело


1
Проблема вашого підходу полягає в тому, що Enumerable.Concatреалізовано System.Linq.Enumerableі повертається значення цього методу IEnumerable<TSource>, тому я вважаю, що його не слід повертати назад IList<TSource>- він може повернути щось інше через деталі реалізації, які ми не знаємо без перевірки вихідного коду - навіть незважаючи на те, що немає жодних гарантій, що це не зміниться - тому слід приділяти особливу увагу при підтримці декількох версій .NET.
jweyrich

8

Ви також можете написати метод розширення таким чином:

internal static class EnumerableHelpers
{
    public static void AddRange<T>(this IList<T> collection, IEnumerable<T> items)
    {
        foreach (var item in items)
        {
            collection.Add(item);
        }
    }
}

Використання:

        IList<int> collection = new int[10]; //Or any other IList
        var items = new[] {1, 4, 5, 6, 7};
        collection.AddRange(items);

Що все ще повторюється над елементами, але вам не потрібно писати ітерацію чи приворот кожного разу, коли ви її викликаєте.


2

Інша відповідь за допомогою LINQ, за умови, що ви додаєте те, List<T>або ви можете ToList()за ним зателефонувати :

IEnumerable<string> toAdd = new string[] {"a", "b", "c"};
IList<string> target = new List<string>();

toAdd.ToList().ForEach(target.Add);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.