LINQ + Foreach vs Foreach + If

我需要迭代对象列表,只对布尔属性设置为true的对象执行某些操作。 我在这个代码之间辩论

foreach (RouteParameter parameter in parameters.Where(p => p.Condition)) { //do something } 

和这个代码

 foreach (RouteParameter parameter in parameters) { if !parameter.Condition continue; //do something } 

第一个代码显然更清晰,但我怀疑它会遍历列表两次 – 一次是查询,一次是foreach。 这不会是一个巨大的名单,所以我不是过度关心性能,但循环两次的想法只是错误的我。

问:有没有干净/漂亮的方式来写这个没有循环两次?

Jon Skeet有时会做一个实际的LINQ演示来解释这是如何工作的。 想象一下,你有三个人在舞台上。 在左边,我们有一个人洗牌一套。 中间我们有一个只传红牌的人,右边有一个人要卡。

右边的那个人捅了中间的那个人。 中间的那个人捅了一下左边的那个人。 左边的那个人把中间的那个人交给了一张牌。 如果是黑色的,中间的人会把它扔到地板上,然后再次捅出来,直到他拿到一张红牌,然后交给右边的那个人。 然后,右边的那个人再次捅了中间的那个人。

这样继续下去,直到左边的人用完卡片。

甲板从头到尾都没有经过多次。 然而,左边的人和中间的人都拿了52张牌,右边的人拿了26张牌。 卡上总共有52 + 52 + 26的行动,但是这个套牌只有一次循环

你的“LINQ”版本和“继续”版本是一回事; 如果你有

 foreach(var card in deck) { if (card.IsBlack) continue; ... use card ... 

那么从卡组中取出每张卡片的操作有52次,testing每张卡片是否黑色的52次操作以及作用在红色卡片上的26次操作。 一样的东西。

大多数Linq运算符,比如Where实现来支持延迟执行和懒惰执行 。 在你的例子中,这个列表将被迭代一次,因为位于由Where返回的IEnumerable后面的枚举器将列举该列表,直到find与谓词相匹配的项目为止,并且只有当它被询问下一个元素时才会继续。

从代码的angular度来看,我更喜欢使用where的变体,虽然可以争辩说你可以声明一个局部parameters.Where(p => p.Condition)

强烈推荐Jon Skeet的Edulinq系列,阅读这些内容可以帮助你理解LINQ操作。

事实上,这不是“循环两次”。 .Where子句使用延迟执行。 换句话说,当你调用的时候,实际上没有任何工作会被执行.Where但是当你迭代结果时,它将迭代原始列表,只传递符合条件的项目。 如果你从代码执行的angular度来考虑它,你可以这样做:

 Func<Parameter, bool> matchesCondition = p => p.Condition; foreach(var parameter in parameters) { if(matchesCondition(parameter)) { ... } } 

作为一个风格问题,我个人更喜欢的东西是:

 var matchingParameters = parameters.Where(p => p.Condition); foreach(var parameter in matchingParameters) { } 

我更喜欢这个:

 theList.Where(itm => itm.Condition).ToList().ForEach(itmFE => { itmFe.DoSomething(); });