Вдохновлен еще одним вопросом о недостающей Zip
функции:
Почему ForEach
в Enumerable
классе нет метода расширения ? Или где угодно? Единственный класс, который получает ForEach
метод List<>
. Есть ли причина, по которой он отсутствует (производительность)?
c#
.net
vb.net
extension-methods
Кэмерон МакФарланд
источник
источник
Parallel.ForEach
дляEnumerable.ForEach
того, чтобы заново обнаружить, что последний не существует. C # пропустил трюк, чтобы упростить ситуацию.Ответы:
Уже есть выражение foreach, включенное в язык, который выполняет работу большую часть времени.
Я бы не хотел видеть следующее:
Вместо:
Последний в большинстве случаев более понятен и удобен для чтения , хотя, возможно, он печатается немного дольше.
Однако я должен признать, что изменил свою позицию по этому вопросу; метод расширения ForEach () действительно может быть полезен в некоторых ситуациях.
Вот основные различия между оператором и методом:
Это все замечательные замечания, сделанные многими людьми здесь, и я понимаю, почему людям не хватает этой функции. Я не против, чтобы Microsoft добавила стандартный метод ForEach в следующей итерации фреймворка.
источник
foreach
Тип проверяется во время компиляции, если перечислимое параметризовано. В нем отсутствует только проверка типов времени компиляции для неуниверсальных коллекций, как и в случае гипотетическогоForEach
метода расширения.col.Where(...).OrderBy(...).ForEach(...)
. Гораздо более простой синтаксис, отсутствие загрязнения экрана ни избыточными локальными переменными, ни длинными скобками в foreach ().list.ForEach(item => item.DoSomething())
. И кто сказал, что это список? Очевидный вариант использования находится в конце цепочки; с оператором foreach вы должны будете поместить всю цепочку послеin
, а операцию выполнить внутри блока, что гораздо менее естественно или непротиворечиво.Метод ForEach был добавлен до LINQ. Если вы добавите расширение ForEach, оно никогда не будет вызываться для экземпляров List из-за ограничений методов расширения. Я думаю, что причина, по которой он не был добавлен, заключается в том, чтобы не мешать существующему.
Однако, если вы действительно пропустите эту маленькую приятную функцию, вы можете развернуть свою собственную версию
источник
Вы можете написать этот метод расширения:
Pros
Позволяет цепочки:
Cons
Это на самом деле ничего не будет делать, пока вы не сделаете что-то, чтобы вызвать итерацию. По этой причине его не следует называть
.ForEach()
. Вы можете написать.ToList()
в конце, или вы можете написать этот метод расширения тоже:Это может быть слишком существенным отклонением от поставляемых библиотек C #; читатели, которые не знакомы с вашими методами расширения, не будут знать, что делать с вашим кодом.
источник
{foreach (var e in source) {action(e);} return source;}
numbers.Select(n => n*2);
Select()
говорит, применить функцию к каждому элементу и вернуть значение функции, гдеApply()
говорит применитьAction
к каждому элементу и вернуть исходный элемент.Select(e => { action(e); return e; })
Обсуждение здесь дает ответ:
В основном было принято решение сохранить функциональные методы расширения "чистыми". ForEach будет стимулировать побочные эффекты при использовании методов расширения Enumerable, что не является целью.
источник
Хотя я согласен с тем, что
примерforeach
в большинстве случаев лучше использовать встроенную конструкцию, я считаю, что использование этого варианта в расширении ForEach <> немного приятнее, чем необходимость управлять индексом в обычномforeach
файле самостоятельно:Даст вам:
источник
Один из способов это написать
.ToList().ForEach(x => ...)
.профи
Легко понять - читателю нужно только знать, что поставляется с C #, а не какие-либо дополнительные методы расширения.
Синтаксический шум очень мягкий (только добавляет немного постороннего кода).
Обычно не стоит дополнительной памяти, так как в
.ForEach()
любом случае нативу придется реализовать всю коллекцию.минусы
Порядок операций не идеален. Я предпочел бы реализовать один элемент, затем действовать на него, а затем повторить. Этот код сначала реализует все элементы, а затем воздействует на них по очереди.
Если реализация списка вызывает исключение, вы никогда не сможете воздействовать на один элемент.
Если перечисление бесконечно (как натуральные числа), вам не повезло.
источник
Enumerable.ForEach<T>
метода, естьList<T>.ForEach
метод. c) «Обычно не стоит дополнительной памяти, поскольку нативный .ForEach () в любом случае должен был бы реализовать всю коллекцию». - полная чушь. Вот реализация Enumerable.ForEach <T>, которую C # обеспечил бы, если бы это было сделано:public static void ForEach<T>(this IEnumerable<T> list, Action<T> action) { foreach (T item in list) action(item); }
Я всегда удивлялся этому сам, поэтому всегда ношу это с собой:
Хороший метод расширения.
источник
Поэтому было много комментариев о том, что метод расширения ForEach не подходит, потому что он не возвращает значение, подобное методам расширения LINQ. Хотя это фактическое утверждение, оно не совсем верно.
Все методы расширения LINQ возвращают значение, поэтому их можно объединить в цепочку:
Однако то, что LINQ реализован с использованием методов расширения, не означает, что методы расширения должны использоваться одинаково и возвращать значение. Написание метода расширения для предоставления общих функций, которые не возвращают значение, является вполне допустимым использованием.
Конкретный аргумент о ForEach заключается в том, что, основываясь на ограничениях на методы расширения (а именно на том, что метод расширения никогда не будет переопределять унаследованный метод с той же сигнатурой ), может возникнуть ситуация, когда пользовательский метод расширения доступен во всех классах, которые реализуют IEnumerable
<T
> кроме списка<T
>. Это может вызвать путаницу, когда методы начинают вести себя по-разному в зависимости от того, вызывается ли метод расширения или метод наследования.источник
Вы можете использовать (цепочку, но лениво оценивать)
Select
, сначала выполняя свою операцию, а затем возвращая идентичность (или что-то еще, если вы предпочитаете)Вам нужно будет убедиться, что он все еще оценивается, либо с
Count()
(самой дешевой операции для перечисления afaik), либо с помощью другой операции, которая вам в любом случае понадобилась.Я хотел бы, чтобы это было внесено в стандартную библиотеку:
Вышеприведенный код становится таким,
people.WithLazySideEffect(p => Console.WriteLine(p))
который фактически эквивалентен foreach, но ленив и цепочек.источник
Select
он больше не делает то, что должен делать. это определенно решение для того, о чем просит OP - но в отношении читабельности темы: никто не ожидал бы, что select выполняет функцию.Select
не делать то, что он должен делать, это проблема с реализациейSelect
, а не с кодом, который вызываетSelect
, который не влияет на то, чтоSelect
делает.Обратите внимание, что MoreLINQ NuGet предоставляет
ForEach
метод расширения, который вы ищете (а такжеPipe
метод, который выполняет делегат и выдает его результат). Видеть:источник
@Coincoin
Реальная сила метода расширения foreach заключается в возможности повторного использования
Action<>
без добавления ненужных методов в ваш код. Скажем, у вас есть 10 списков, и вы хотите выполнять с ними одинаковую логику, а соответствующая функция не вписывается в ваш класс и не используется повторно. Вместо десяти циклов for или универсальной функции, которая, очевидно, является вспомогательным и не принадлежит, вы можете хранить всю свою логику в одном месте (Action<>
. Таким образом, десятки строк заменяются натак далее...
Логика в одном месте, и вы не загрязнили свой класс.
источник
f(p)
, затем оставить вне{ }
для стенографии:for (var p in List1.Concat(List2).Concat(List2)) f(p);
.Большинство методов расширения LINQ возвращают результаты. ForEach не вписывается в этот шаблон, поскольку он ничего не возвращает.
источник
public IEnumerable<T> Foreach<T>(this IEnumerable<T> items, Action<T> action) { /* error check */ foreach (var item in items) { action(item); yield return item; } }
Если у вас есть F # (который будет в следующей версии .NET), вы можете использовать
Seq.iter doSomething myIEnumerable
источник
Частично это потому, что дизайнеры языка не согласны с этим с философской точки зрения.
https://blogs.msdn.microsoft.com/ericlippert/2009/05/18/foreach-vs-foreach/
источник
Вы можете использовать select, если хотите что-то вернуть. Если вы этого не сделаете, вы можете сначала использовать ToList, потому что вы, вероятно, не хотите ничего изменять в коллекции.
источник
Я написал в блоге об этом: http://blogs.msdn.com/kirillosenkov/archive/2009/01/31/foreach.aspx
Вы можете проголосовать здесь, если хотите увидеть этот метод в .NET 4.0: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=279093
источник
В 3.5 все методы расширения, добавленные в IEnumerable, предназначены для поддержки LINQ (обратите внимание, что они определены в классе System.Linq.Enumerable). В этом посте я объясню, почему foreach не принадлежит LINQ: существующий метод расширения LINQ похож на Parallel.For?
источник
Это я или это List <T>. Потому что Линк почти устарел. Первоначально был
где Y просто должен был быть IEnumerable (Pre 2.0) и реализовывать GetEnumerator (). Если вы посмотрите на сгенерированный MSIL, вы увидите, что он точно такой же, как
(См. Http://alski.net/post/0a-for-foreach-forFirst-forLast0a-0a-.aspx для MSIL)
Затем в DotNet2.0 появились Generics и список. Foreach всегда чувствовал, что я являюсь реализацией шаблона Vistor (см. Шаблоны проектирования от Gamma, Helm, Johnson, Vlissides).
Теперь, конечно, в 3.5 мы можем вместо этого использовать лямбду для того же эффекта, например, попробуйте http://dotnet-developments.blogs.techtarget.com/2008/09/02/iterators-lambda-and-linq-oh- мой /
источник
Я хотел бы расширить ответ Аку .
Если вы хотите вызвать метод с единственной целью его побочного эффекта, не повторяя сначала все перечисляемое, вы можете использовать это:
источник
Select(x => { f(x); return x;})
Никто еще не указал, что ForEach <T> приводит к проверке типов во время компиляции, где ключевое слово foreach проверяется во время выполнения.
Сделав некоторый рефакторинг, где оба метода использовались в коде, я предпочитаю .ForEach, так как мне приходилось выслеживать сбои тестов / сбои во время выполнения, чтобы найти проблемы foreach.
источник
foreach
можно меньше проверять время компиляции. Конечно, если ваша коллекция не является универсальной IEnumerable, переменная цикла будетobject
, но то же самое будет верно дляForEach
метода гипотетического расширения.