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

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


当前回答

是我还是List<T>。Foreach几乎被Linq淘汰了。 最初有

foreach(X x in Y) 

其中Y必须是IEnumerable (Pre 2.0),并实现GetEnumerator()。 如果您查看生成的MSIL,您可以看到它与

IEnumerator<int> enumerator = list.GetEnumerator();
while (enumerator.MoveNext())
{
    int i = enumerator.Current;

    Console.WriteLine(i);
}

(MSIL见http://alski.net/post/0a-for-foreach-forFirst-forLast0a-0a-.aspx)

然后在DotNet2.0中出现了泛型和列表。我一直觉得Foreach是访问者模式的实现(参见Gamma, Helm, Johnson, Vlissides的设计模式)。

当然,在3.5中,我们可以使用Lambda来达到同样的效果,举个例子 http://dotnet-developments.blogs.techtarget.com/2008/09/02/iterators-lambda-and-linq-oh-my/

其他回答

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

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扩展方法都返回结果。ForEach不符合这个模式,因为它什么也不返回。

我的版本是一个扩展方法,允许你在T的IEnumerable上使用ForEach

public static class EnumerableExtension
{
        public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
    {
        source.All(x =>
        {
            action.Invoke(x);
            return true;
        });
    }
}

这里的讨论给出了答案:

实际上,我所看到的具体讨论确实取决于功能的纯洁性。在一个表达式中,经常会有关于没有副作用的假设。使用ForEach是特别吸引副作用,而不仅仅是忍受它们。——基思·法默(合伙人)

基本上,这个决定是为了保持扩展方法在功能上的“纯粹”。ForEach在使用Enumerable扩展方法时会产生副作用,这不是目的。

虽然我同意在大多数情况下使用内置的foreach构造更好,但我发现在foreach <>扩展上使用这种变体比自己在常规foreach中管理索引要好一些:

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

    var index = 0;

    foreach (var elem in list)
        action(index++, elem);

    return index;
}
Example
var people = new[] { "Moe", "Curly", "Larry" };
people.ForEach((i, p) => Console.WriteLine("Person #{0} is {1}", i, p));

会给你:

Person #0 is Moe
Person #1 is Curly
Person #2 is Larry