Подтвердить что ты не робот

Есть ли какой-либо метод, например, ForEach для IList?

Возможный дубликат:
LINQ эквивалент foreach для IEnumerable <T>

List<T> имеет метод под названием ForEach, который выполняет переданное действие для каждого его элемента.

var names = new List<String>{ "Bruce", "Alfred", "Tim", "Richard" };

names.ForEach(p =>  { Console.WriteLine(p); });

Но что, если names не является List<T>, но IList<T>, IList<T> не имеет метода вроде ForEach. Есть ли альтернатива?

4b9b3361

Ответ 1

Используйте цикл foreach:

foreach (var p in names) {
    Console.WriteLine(p);
}

Нет причин использовать делегаты и методы расширения повсюду, если на самом деле это не улучшает читаемость; a foreach цикл не менее явно говорит читателям, что делается, чем метод foreach.

Ответ 2

Если ваш IList<T> является массивом (T[]), то вы Array.ForEach на них похожи на ForEach на List<T>. Вы можете создать метод расширения для своих пользовательских IList<T> или IEnumerable<T> или того, что вы предпочитаете.

public static void ForEach<T>(this IList<T> list, Action<T> action)
{
    foreach (T t in list)
        action(t);
}

Вам просто нужно быть осторожным в том, что объекты в исходной коллекции будут изменены, но я думаю, что это именование подразумевает это.

-------------------------------------------- -------------------------------------------------- --------------------------------------

Я предпочитаю звонить:

people.Where(p => p.Tenure > 5)
      .Select(p => p.Nationality)
      .ForEach(n => AssignCitizenShip(n);

чем

foreach (var n in people.Where(p => p.Tenure > 5).Select(p => p.Nationality))
{
    AssignCitizenShip(n);
}

Если это так, вы можете создать метод расширения на IEnumerable. Имейте в виду, что завершающий вызов ForEach выполняет запрос Linq. Если вы этого не хотите, вы можете отложить его также с помощью инструкции yield и возврата IEnumerable<T> назад:

public static IEnumerable<T> ForEach<T>(this IEnumerable<T> list, Action<T> action)
{
    foreach (T t in list)
    {
        action(t);
        yield return t;
    }
}

Это решает проблему побочного эффекта, но мне лично нравится метод с именем ForEach для окончательного выполнения вызова.

-------------------------------------------- -------------------------------------------------- -------------------------------------

Чтобы рассмотреть противоположные взгляды на предпочтения, вот лучшая ссылка от Эрика Липперта, чем this. Процитировать его:

"Первая причина заключается в том, что это нарушает функциональное программирование принципы, на которых основаны все другие операторы последовательности. Ясно, что единственной целью вызова этого метода является причина последствия. Цель выражения - вычислить значение, а не вызывают побочный эффект. Цель заявления - вызвать сторону эффект. Сайт вызова этой вещи выглядел бы ужасно (хотя, по общему признанию, поскольку метод недействителен, выражение может использоваться только в выражении" выражение "контекст.) Он не подходит мне, чтобы сделать одно и то же который полезен только для его побочных эффектов.

Вторая причина заключается в том, что при этом добавляется ноль новых репрезентативных власть на язык".

Эрик не говорит, что это плохо, а именно философские причины решения не включать конструкцию в Linq по умолчанию. Если вы считаете, что функция на IEnumerable не должна воздействовать на содержимое, тогда не делайте этого. Лично я не возражаю против этого, так как я хорошо знаю, что он делает. Я рассматриваю его как любой другой метод, который вызывает побочный эффект для класса коллекции. Я могу войти в функцию и отладить ее, если захочу. Вот еще один из Linq.

people.Where(p => p.Tenure > 5)
      .Select(p => p.Nationality)
      .AsParallel()
      .ForAll(n => AssignCitizenShip(n);

Как я бы сказал, в этом нет ничего плохого. Это просто личное предпочтение. Я бы не использовал это для вложенных ForEach или если он включает более одной строки кода для выполнения внутри цикла ForEach, так как это просто нечитабельно. Но для простого примера, который я опубликовал, мне нравится Это. Выглядит чисто и лаконично.

Изменить: См. ссылку на производительность btw: Почему List <T> .ForEach быстрее, чем стандартный foreach?

Ответ 3

Вы можете создать метод расширения и использовать большую часть реализации void List<T>.ForEach(Action<T> action). Вы можете загрузить исходный код на сайте с инициативой об общих источниках.

В основном вы закончите что-то вроде этого:

public static void ForEach<T>(this IList<T> list, Action<T> action) 
{
    if (list == null) throw new ArgumentNullException("null");
    if (action == null) throw new ArgumentNullException("action");

    for (int i = 0; i < list.Count; i++)
    {
        action(list[i]);
    }
}

Он немного лучше, чем другие реализации, которые используют оператор foreach, поскольку он использует тот факт, что IList включает в себя индекс.

Несмотря на то, что я согласен с ответом О. Р. Картера, иногда в больших проектах со многими разработчиками трудно убедить всех, что оператор foreach более ясен. Хуже того, если ваш API основан на интерфейсах (IList) вместо конкретных типов (List), то разработчики, которые используются для List<T>.ForEach method, могут начать звонить ToList по вашим ссылкам IList! Я знаю, потому что это произошло в моем предыдущем проекте. Я использовал интерфейсы коллекции повсюду в наших общедоступных API-интерфейсах, следуя Правилам проектирования каркаса. Мне потребовалось некоторое время, чтобы заметить, что многие разработчики, не привыкшие к этому, и звонящие на ToList, начали с угрожающей скоростью. Наконец, я добавил этот метод расширения к общей сборке, которую все использовали, и удостоверился, что все ненужные вызовы ToList были удалены из кодовой базы.

Ответ 4

Добавьте этот код в статический класс и вызовите его:

public static void ForEach<T>(this IList<T> list, Action<T> action) {
    foreach(var item in list) {
        action.Invoke(item);
    }
}