Why there is no ForEach extension method on IEnume

2018-12-31 18:07发布

Inspired by another question asking about the missing Zip function:

Why is there no ForEach extension method in the Enumerable class? Or anywhere? The only class that gets a ForEach method is List<>. Is there a reason why it's missing (performance)?

21条回答
情到深处是孤独
2楼-- · 2018-12-31 18:19

I've always wondered that myself, that is why that I always carry this with me:

public static void ForEach<T>(this IEnumerable<T> col, Action<T> action)
{
    if (action == null)
    {
        throw new ArgumentNullException("action");
    }
    foreach (var item in col)
    {
        action(item);
    }
}

Nice little extension method.

查看更多
看淡一切
3楼-- · 2018-12-31 18:19

Partially it's because the language designers disagree with it from a philosophical perspective.

  • Not having (and testing...) a feature is less work than having a feature.
  • It's not really shorter (there's some passing function cases where it is, but that wouldn't be the primary use).
  • It's purpose is to have side effects, which isn't what linq is about.
  • Why have another way to do the same thing as a feature we've already got? (foreach keyword)

https://blogs.msdn.microsoft.com/ericlippert/2009/05/18/foreach-vs-foreach/

查看更多
一个人的天荒地老
4楼-- · 2018-12-31 18:20

I would like to expand on Aku's answer.

If you want to call a method for the sole purpose of it's side-effect without iterating the whole enumerable first you can use this:

private static IEnumerable<T> ForEach<T>(IEnumerable<T> xs, Action<T> f) {
    foreach (var x in xs) {
        f(x); yield return x;
    }
}
查看更多
长期被迫恋爱
5楼-- · 2018-12-31 18:24

Note that the MoreLINQ NuGet provides the ForEach extension method you're looking for (as well as a Pipe method which executes the delegate and yields its result). See:

查看更多
十年一品温如言
6楼-- · 2018-12-31 18:26

The discussion here gives the answer:

Actually, the specific discussion I witnessed did in fact hinge over functional purity. In an expression, there are frequently assumptions made about not having side-effects. Having ForEach is specifically inviting side-effects rather than just putting up with them. -- Keith Farmer (Partner)

Basically the decision was made to keep the extension methods functionally "pure". A ForEach would encourage side-effects when using the Enumerable extension methods, which was not the intent.

查看更多
步步皆殇っ
7楼-- · 2018-12-31 18:26

One workaround is to write .ToList().ForEach(x => ...).

pros

Easy to understand - reader only needs to know what ships with C#, not any additional extension methods.

Syntactic noise is very mild (only adds a little extranious code).

Doesn't usually cost extra memory, since a native .ForEach() would have to realize the whole collection, anyway.

cons

Order of operations isn't ideal. I'd rather realize one element, then act on it, then repeat. This code realizes all elements first, then acts on them each in sequence.

If realizing the list throws an exception, you never get to act on a single element.

If the enumeration is infinite (like the natural numbers), you're out of luck.

查看更多
登录 后发表回答