受到另一个关于缺失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))

等等……

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

其他回答

当你想要返回一些东西时,你可以使用select。 如果不需要,可以先使用ToList,因为您可能不想修改集合中的任何内容。

因此,有很多关于ForEach扩展方法不合适的评论,因为它不像LINQ扩展方法那样返回值。虽然这是事实陈述,但并不完全正确。

LINQ扩展方法都返回一个值,所以它们可以链接在一起:

collection.Where(i => i.Name = "hello").Select(i => i.FullName);

然而,仅仅因为LINQ是使用扩展方法实现的,并不意味着扩展方法必须以相同的方式使用并返回一个值。编写一个扩展方法来公开不返回值的常见功能是完全有效的使用。

关于ForEach的具体参数是,基于扩展方法的约束(即扩展方法永远不会覆盖具有相同签名的继承方法),可能存在这样一种情况,即自定义扩展方法在所有impelement IEnumerable<T> (List<T>除外)的类上可用。这可能会导致混淆,因为根据调用的是扩展方法还是继承方法,方法开始表现不同。

是我还是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/

您可以使用(可链的,但延迟计算)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,但懒惰和可链。

你可以这样写扩展方法:

// Possibly call this "Do"
IEnumerable<T> Apply<T> (this IEnumerable<T> source, Action<T> action)
{
    foreach (var e in source)
    {
        action(e);
        yield return e;
    }
}

Pros

允许链接:

MySequence
    .Apply(...)
    .Apply(...)
    .Apply(...);

Cons

它实际上不会做任何事情,直到你强制迭代。因此,它不应该被称为. foreach()。你可以在结尾写.ToList(),或者你也可以写这个扩展方法:

// possibly call this "Realize"
IEnumerable<T> Done<T> (this IEnumerable<T> source)
{
    foreach (var e in source)
    {
        // do nothing
        ;
    }

    return source;
}

这可能与发布的c#库有很大的不同;不熟悉您的扩展方法的读者将不知道如何理解您的代码。