受到另一个关于缺失Zip函数的问题的启发:

为什么在IEnumerable接口上没有ForEach扩展方法?或在任何地方吗?唯一获得ForEach方法的类是List<>。有什么原因吗,也许是性能?


当前回答

@Coincoin

foreach扩展方法的真正功能包括Action<>的可重用性,而无需向代码添加不必要的方法。假设您有10个列表,并且您希望对它们执行相同的逻辑,而对应的函数不适合您的类并且不能重用。您可以将所有逻辑保存在一个地方,而不是使用十个for循环,或者使用一个显然不属于helper的通用函数(Action<>。所以,很多行被替换成

Action<blah,blah> f = { foo };

List1.ForEach(p => f(p))
List2.ForEach(p => f(p))

等等……

逻辑是在一个地方,你没有污染你的课。

其他回答

我自己也一直在想,这就是为什么我总是带着这个:

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);
    }
}

不错的扩展方法。

部分原因是语言设计者从哲学角度不同意这一观点。

没有(和测试……)一个功能比有一个功能更省事。 它并不是真的更短(有一些传递函数的情况下,它是,但这不是主要用途)。 它的目的是产生副作用,这不是linq的目的。 为什么要用另一种方式来做我们已经拥有的功能呢?(为每一个关键字)

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

ForEach方法在LINQ之前添加。如果您添加ForEach扩展,由于扩展方法的约束,它将永远不会被用于List实例。我认为没有添加的原因是为了不干扰现有的。

但是,如果您真的错过了这个漂亮的小函数,您可以推出自己的版本

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

一种解决方法是编写. tolist()。ForEach(x =>…)

pros

易于理解——读者只需要知道c#附带了什么,而不需要知道任何额外的扩展方法。

语法干扰非常温和(只添加了一点无关的代码)。

通常不需要额外的内存,因为本机. foreach()必须实现整个集合。

cons

操作顺序并不理想。我宁愿实现一个元素,然后付诸行动,然后重复。这段代码首先实现所有元素,然后依次对它们进行操作。

如果意识到列表抛出异常,则永远无法对单个元素进行操作。

如果枚举数是无限的(就像自然数一样),那么你就不走运了。

您可以使用(可链的,但延迟计算)Select,首先执行操作,然后返回标识(如果您愿意,也可以返回其他内容)

IEnumerable<string> people = new List<string>(){"alica", "bob", "john", "pete"};
people.Select(p => { Console.WriteLine(p); return p; });

您需要确保它仍然被求值,可以使用Count()(枚举afaik最便宜的操作),也可以使用其他您需要的操作。

不过,我希望看到它被引入标准库:

static IEnumerable<T> WithLazySideEffect(this IEnumerable<T> src, Action<T> action) {
  return src.Select(i => { action(i); return i; } );
}

上面的代码就变成了人。WithLazySideEffect(p => Console.WriteLine(p)),这实际上相当于foreach,但懒惰和可链。