foreach循环和ForEach方法有什么区别?

12

使用 foreach 循环和 LINQForEach 方法,是否有性能或其他方面的区别?

为了背景,这是我的方法的一部分:

foreach (var property in typeof(Person).GetProperties())
{
    Validate(property.Name);
}

我可以使用以下代码来执行相同的任务:

typeof(Person)
    .GetProperties()
    .ToList()
    .ForEach(property => Validate(property.Name));

使用循环结构比使用方法链更好的情况是什么?

下面是另一个例子,我在这里使用了 ForEach 方法,但同样可以使用 foreach 循环和变量:

// LINQ
PrivateData.Database.Users
           .Cast<User>()
           .Where(user => user.LoginType == LoginType.WindowsUser)
           .Select(user => new { Name = user.Name, Login = user.Login })
           .ToList()
           .ForEach(result => WriteObject(result));

// Loop
var users = PrivateData.Database.Users
               .Cast<User>()
               .Where(user => user.LoginType == LoginType.WindowsUser)
               .Select(user => new { Name = user.Name, Login = user.Login });

foreach(var user in users)
{
    WriteObject(user);
}

2
请注意,在LINQ风格中,您必须在使用ForEach之前调用ToList - 像这样迭代并执行操作并不是真正的“LINQy”方式。 - James Thorpe
1
@Thomas LINQ并没有ForEach方法。正确的LINQ做法是使用一个foreach循环。 - Servy
1
什么是检查你携带多少钱的更快方法?1:打开你的钱包看看钞票。2:买一个新钱包,复印钞票放入新钱包,然后查看复印件。在软件工程术语中:您使用ToList()只是为了使用ForEach而产生O(n)存储成本。.NET和现代计算机使存储变得便宜,但并非免费。 - Hans Passant
1
  1. 可能该方法是“void”,因此将无效。
  2. 这意味着查询永远不会被迭代,因此该方法永远不会运行。
- Servy
1
考虑在数据库上使用 ToList,如果结果有数十亿条记录,在 foreach 执行之前就会消耗完内存。相反,在原始查询上使用 foreach 将一次加载每个记录。 - Cameron MacFarland
显示剩余9条评论
3个回答

12
我建议您参考Eric Lippert的博客“foreach”vs“ForEach”。作为以前 C# 编译器团队的首席开发人员,我认为他的观点非常准确。

摘录:(关于.ForEach()

首先,这样做违反了所有其他序列操作符所基于的函数式编程原则。显然,调用此方法的唯一目的是引起副作用。表达式的目的是计算值,而不是引起副作用。语句的目的是引起副作用。该方法的调用位置看起来很像表达式(尽管可以承认,由于该方法是 void 返回的,该表达式只能在“语句表达式”上下文中使用)。让我感到不舒服的是,使唯一一个只有在其副作用方面有用的序列运算符。


1

循环更好的风格,因为它是一个完全为您想做的事情而设计的工具。它更好地与语言集成。例如,您可以从循环中退出。工具理解循环,而不理解ForEach

循环对于人类来说也更容易理解。ForEach非常不常见。

循环也更快,因为间接调用较少,委托分配较少,并且优化器可以在一个地方看到您正在做的更多内容。它还保存了ToList调用。您也可以通过编写IEnumerable<T>上的自定义扩展方法来保存该调用。

我认为在我所能想到的所有情况下,循环都是更优越的。也许有一些角落情况,其中ForEach方法可能更好或出于某种原因更方便。

PLINQ还有一个ForAll,由于其可以并行化,因此在效率方面是必需的。


“少一些委托分配”,那么委托将被编译为一个方法,不是吗?这意味着它与使用普通方法调用的 foreach 没有任何区别。唯一的区别在于 .ToList(),正如您所说,可以通过自定义扩展方法来解决... - Phill
@Phill Lambda 将被编译为一个方法。然后在构造新分配的委托时使用该方法。 - Servy
我不是很明白,因为他的例子也可以写成:.ForEach(WriteObject),我相信这应该编译成大致相同的IL代码。(如果我完成手头的工作,我可能会出于好奇尝试一下) - Phill
@Phill 那段代码确实需要分配一个委托,你说得完全正确。这就是 foreachForEach 之间的区别,与 lambda 的使用无关。 - Servy
因为这主要是关于个人对一种选择的偏好的意见回答,所以被踩了。 - David Arno
@DavidArno 我所说的一切都是客观无争议的。请指出主观性的例子。 - usr

-4

在大多数情况下,这是个人偏好的问题。讨论性能方面:

多数情况下,由于引入了开销,LINQ 会稍微慢一些。如果您非常关注性能,请不要使用 LINQ。使用 LINQ 是因为您希望代码更短、易读且易于维护。


1
ForEach 不是 LINQ。 - Rawling
通过指定 LINQ 引入的开销以及在哪些情况下包括该开销,可以改进此答案。 - Joshcodes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接