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

Выполнение определенного действия для всех элементов в Enumerable <T>

У меня есть Enumerable<T> и я ищу метод, который позволяет мне выполнить действие для каждого элемента, вроде как Select, но затем для побочных эффектов. Что-то вроде:

string[] Names = ...;
Names.each(s => Console.Writeline(s));

или

Names.each(s => GenHTMLOutput(s));   
// (where GenHTMLOutput cannot for some reason receive the enumerable itself as a parameter)

Я попробовал Select(s=> { Console.WriteLine(s); return s; }), но ничего не печатал.

4b9b3361

Ответ 1

Быстрый и простой способ получить это:

Names.ToList().ForEach(e => ...);

Ответ 2

Вы ищете постоянно неуловимый ForEach, который в настоящее время существует только в общей коллекции List. В Интернете много дискуссий о том, следует ли Microsoft или не следует добавлять это как метод LINQ. В настоящее время вы должны сворачивать самостоятельно:

public static void ForEach<T>(this IEnumerable<T> value, Action<T> action)
{
  foreach (T item in value)
  {
    action(item);
  }
}

В то время как метод All() предоставляет аналогичные возможности, он используется для выполнения теста предикатов для каждого элемента, а не для действия. Разумеется, его можно убедить выполнять другие задачи, но это несколько изменяет семантику и затруднит интерпретацию вашего кода другими пользователями (т.е. Это использование All() для теста предикатов или действия?).

Ответ 3

Disclaimer: This post no longer resembles my original answer, but rather incorporates the some seven years experience I've gained since. I made the edit because this is a highly-viewed question and none of the existing answers really covered all the angles. If you want to see my original answer, it available in the revision history for this post.


Первое, что нужно понять, это операции С# linq, такие как Select(), All(), Where() и т.д., Имеют свои корни в функциональном программировании. Идея заключалась в том, чтобы перенести некоторые из наиболее полезных и доступных частей функционального программирования в мир .Net. Это важно, потому что ключевым принципом функционального программирования является отсутствие побочных эффектов в операциях. Трудно преуменьшить это. Тем не менее, в случае ForEach()/each(), побочные эффекты являются основной причиной операции. Добавление each() или ForEach() не только выходит за рамки функционального программирования других операторов linq, но и прямо противоположно им.

Но я понимаю, что это неудовлетворительно. У вас есть реальная проблема, которую вам нужно решить. Почему вся эта философия башни из слоновой кости должна мешать чему-то, что может быть действительно полезным?

Эрик Липперт, который в то время работал в команде разработчиков С#, может помочь нам здесь. Он рекомендует использовать традиционный цикл foreach:

[ForEach()] добавляет ноль новых возможностей представления в язык. Это позволяет вам переписать этот совершенно чистый код:

foreach(Foo foo in foos){ statement involving foo; }

в этот код:

foos.ForEach(foo=>{ statement involving foo; });

Суть его в том, что если вы внимательно посмотрите на параметры синтаксиса, вы не получите ничего нового от расширения ForEach() по сравнению с традиционным циклом foreach. Я частично не согласен. Представьте, что у вас есть это:

 foreach(var item in Some.Long(and => possibly)
                         .Complicated(set => ofLINQ)
                         .Expression(to => evaluate)
 {
     // now do something
 } 

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

Some.Long(and => possibly)
   .Complicated(set => ofLINQ)
   .Expression(to => evaluate)
   .ForEach(item => 
{
    // now do something
});

Тем не менее, даже здесь, в конце концов, я пришел к точке зрения Эрика. Я понял, что код, который вы видите выше, вызывает дополнительную переменную. Если у вас есть сложный набор выражений LINQ, подобных этому, вы можете добавить ценную информацию в свой код, сначала присвоив результат выражения LINQ новой переменной:

var queryForSomeThing = Some.Long(and => possibly)
                        .Complicated(set => ofLINQ)
                        .Expressions(to => evaluate);
foreach(var item in queryForSomeThing)
{
    // now do something
}

Этот код кажется более естественным. Он помещает ключевое слово foreach рядом с остальной частью цикла и после определения запроса. Более того, имя переменной может добавить новую информацию, которая будет полезна будущим программистам, пытающимся понять цель запроса LINQ. Опять же, мы видим, что требуемый оператор ForEach() действительно не добавил новой выразительной силы в язык.

Однако нам все еще не хватает двух особенностей гипотетического метода расширения ForEach():

  1. Это не составно. Я не могу добавить еще .Where(), GroupBy() или OrderBy() после цикла foreach, встроенного в остальную часть кода, без создания нового оператора.
  2. Это не ленивый. Эти операции происходят немедленно. Это не позволяет мне, скажем, иметь форму, в которой пользователь выбирает операцию в качестве одного поля на большом экране, на который не воздействуют, пока пользователь не нажмет командную кнопку. Эта форма может позволить пользователю передумать перед выполнением команды. Это совершенно нормально (даже легко) с запросом LINQ, но не так просто с foreach.

Конечно, вы можете создать свой собственный метод расширения ForEach(). Несколько других ответов уже имеют реализации этого метода; это не все так сложно. Однако я чувствую, что это не нужно. Уже существует существующий метод, который соответствует тому, что мы хотим сделать как с семантической, так и с операционной точек зрения. Обе вышеупомянутые недостающие функции могут быть решены с помощью существующего оператора Select().

Select() соответствует виду трансформации или проекции, описанному в обоих приведенных выше примерах. Имейте в виду, однако, что я все равно избегал бы создания побочных эффектов. Вызов Select() должен вернуть либо новые объекты, либо проекции из оригиналов. Иногда этому может помочь использование анонимного типа или динамического объекта (если и только если это необходимо). Если вам нужно, чтобы результаты сохранялись, скажем, в исходной переменной списка, вы всегда можете вызвать .ToList() и присвоить ее обратно вашей исходной переменной. Здесь я добавлю, что я предпочитаю работать с переменными IEnumerable<T> как можно больше над более конкретными типами.

myList = myList.Select(item => new SomeType(item.value1, item.value2 *4)).ToList();

В итоге:

  1. Просто придерживайтесь foreach большую часть времени.
  2. Когда foreach действительно не подходит (что, вероятно, не так часто, как вы думаете), используйте Select()
  3. Когда вам нужно использовать Select(), вы все равно можете избежать (видимых программой) побочных эффектов, возможно, проецируя на анонимный тип.

Ответ 4

К сожалению, нет встроенного способа сделать это в текущей версии LINQ. Команда фреймворков пренебрегла добавлением метода расширения .ForEach. Там хорошая дискуссия об этом происходит прямо сейчас в следующем блоге.

http://blogs.msdn.com/kirillosenkov/archive/2009/01/31/foreach.aspx

Это довольно легко добавить, хотя.

public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action) {
  foreach ( var cur in enumerable ) {
    action(cur);
  }
}

Ответ 5

Вы не можете сразу это сделать с помощью LINQ и IEnumerable - вам нужно либо реализовать свой собственный метод расширения, либо перечислить перечисление в массив с LINQ, а затем вызвать Array.ForEach():

Array.ForEach(MyCollection.ToArray(), x => x.YourMethod());

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

Ответ 6

Поскольку LINQ предназначен для функции запроса, а не для функции обновления, вы не найдете расширение, которое выполняет методы в IEnumerable <T> потому что это позволит вам выполнить метод (потенциально с побочными эффектами). В этом случае вы можете просто придерживаться

foreach (имя строки в именах)
ЕЫпе (имя);

Ответ 7

Ну, вы также можете использовать стандартное ключевое слово foreach, просто отформатируйте его в oneliner:

foreach(var n in Names.Where(blahblah)) DoStuff(n);

Извините, этот вариант заслуживает того, чтобы быть здесь:)

Ответ 8

Использование Parallel Linq:

Имена .AsParallel(). ForAll (name = > ...)

Ответ 9

Существует исключающий метод ForEach из списка. Вы можете преобразовать Enumerable в List, вызвав метод .ToList(), а затем отключите метод ForEach.

В качестве альтернативы, я слышал о том, что люди, определяющие свой собственный метод ForEach, отключены от IEnumerable. Это можно выполнить, по существу, вызывая метод ForEach, но вместо этого обернув его в метод расширения:

public static class IEnumerableExtensions
{
    public static IEnumerable<T> ForEach<T>(this IEnumerable<T> _this, Action<T> del)
    {
        List<T> list = _this.ToList();
        list.ForEach(del);
        return list;
    }
}