LINQ + Foreach vs Foreach + If

时间:2012-01-30 23:02:42

标签: c# linq foreach

我需要迭代一个对象列表,只为布尔属性设置为true的对象做一些事情。我正在讨论这段代码

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

和此代码

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

第一个代码显然更干净,但我怀疑它会在列表上循环两次 - 一次用于查询,一次用于foreach。这不会是一个巨大的列表,所以我不是过分关注性能,而是循环两次只是错误我的想法。

问题:如果没有循环两次,是否有一种干净/漂亮的方式来编写它?

4 个答案:

答案 0 :(得分:126)

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

右边的那个人捅中间的那个人。中间的人戳了左边的那个人。左边的那个人把中间的那个人递给了一张牌。如果它是黑色的,那么中间的那个人把它扔在地板上然后再次戳,直到他得到一张红牌,然后他将牌递给右边的那个人。然后右边的那个人又把那个中间人捅了出来。

这一直持续到左边的那个人用完卡片。

牌组从头到尾不止一次。然而,左边的那个人和中间的人都处理了52张牌,而右边的那个人处理了26张牌牌。卡片上总共有52 + 52 + 26个操作,但卡片只能通过一次

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

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

然后有52个操作从牌组中取出每张牌,52个操作用于测试每张牌是否为黑色,以及26个操作在红牌上。同样的事情。

答案 1 :(得分:35)

大多数Linq运算符(例如Where)都是为了支持延迟执行和延迟执行而实现的。在您的示例中,列表将仅迭代一次,因为位于Where返回的IEnumerable后面的枚举器将枚举列表,直到找到与谓词匹配的项,产生它并且仅在请求时才继续下一个元素。

从代码的角度来看,我更喜欢使用where的变体,虽然可以说你可以为parameters.Where(p => p.Condition)声明一个本地。

Jon Skeet的Edulinq系列强烈推荐,阅读其中的一些内容可以帮助您理解LINQ运算符。

答案 2 :(得分:26)

实际上,它并非“循环两次”。 .Where子句使用延迟执行。换句话说,当你调用.Where时几乎没有工作,但是当你遍历结果时,它会遍历原始列表并只传递符合你条件的项目。如果您根据代码的执行方式来考虑它,那么您实际上是这样做的:

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

答案 3 :(得分:-3)

我更喜欢这个:

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