正确的Linq where子句


133

在我的日常生活中,我写了大量的linq,但主要是简单的陈述。我已经注意到,使用where子句时,有许多种写法,据我所知每种方法都有相同的结果。例如;

from x in Collection
  where x.Age == 10
  where x.Name == "Fido"
  where x.Fat == true
  select x;

至少就结果而言似乎与之等效:

from x in Collection
  where x.Age == 10 &&
        x.Name == "Fido" &&
        x.Fat == true
  select x;

那么,除了语法以外,真的有区别吗?如果是这样,首选样式是什么,为什么?


203
您具有布尔Fat属性?这很简单。
Bala R

104
@Bala R:嘿,如果你的狗胖了,那你的狗胖了。
AR

Answers:


76

第二个会更高效,因为它只有一个谓词可以对集合中的每个项目进行评估,与第一个一样,它将第一个谓词首先应用于所有项目,结果(此时缩小了范围)是用于第二个谓词,依此类推。每次通过都会缩小结果范围,但仍然涉及多次通过。

同样,仅当您对谓词进行“与”运算时,链接(第一种方法)才有效。这样的事情x.Age == 10 || x.Fat == true不适用于您的第一种方法。


1
使用此扩展名可能会产生链ORing条件:albahari.com/nutshell/predicatebuilder.aspx
jahu 2014年

142

编辑:对象的LINQ行为不符合我的预期。您可能对我刚刚写的博客文章感兴趣...


它们在称为什么方面有所不同-第一个等效于:

Collection.Where(x => x.Age == 10)
          .Where(x => x.Name == "Fido")
          .Where(x => x.Fat == true)

后者等同于:

Collection.Where(x => x.Age == 10 && 
                      x.Name == "Fido" &&
                      x.Fat == true)

现在,实际产生的差异取决于Where调用的实现。如果它是一个基于SQL的提供程序,我希望两者最终会创建相同的SQL。如果它在LINQ to Objects中,则第二个将具有较少的间接级别(仅涉及两个迭代器,而不是四个)。这些间接级别在速度方面是否很重要是另一回事。

通常,我会使用多个where子句,如果它们感觉它们代表着截然不同的条件(例如,一个与一个对象的一部分有关,而一个则是完全独立的),而where当各种条件密切相关(例如,一个特定值)时,则使用一个子句大于最小值且小于最大值)。基本上,在任何轻微的性能差异之前,都值得考虑可读性。


1
@JonSkeet也许我错了,但是在快速回顾一下Linq Where的实现后,我不确定。嵌套的位置通过静态方法“ CombinePredicates”组合在一起。单个谓词与组合谓词仅对Collection进行一次迭代。当然,合并func会对性能产生影响,但这是非常有限的。你还好吗 ?
Cyber​​maxs

@Cyber​​maxs:不确定到底什么?我从未建议过对集合进行多次迭代。
乔恩·斯基特

@JonSkeet当然可以,但是最后所有谓词都组合在一起,并且只涉及一个迭代器。查看Enumerable.WhereSelectEnumerableIterator。
Cyber​​maxs

您链接到的页面现已关闭。如果文章仍在其他地方,您能否更新链接?谢谢。
Asad Saeeduddin

2
@Asad:已更新。(我的博客已移动。)
Jon Skeet 2015年

13

第一个将被实施:

Collection.Where(x => x.Age == 10)
          .Where(x => x.Name == "Fido") // applied to the result of the previous
          .Where(x => x.Fat == true)    // applied to the result of the previous

相对于更简单的(和远快可能更快):

// all in one fell swoop
Collection.Where(x => x.Age == 10 && x.Name == "Fido" && x.Fat == true)

6
“快一点”?我们甚至都不知道涉及哪个LINQ实现,因此很难在其上附加任何性能方面的含义。
乔恩·斯基特

在一般情况下,后者仅需要1个循环。提供者可以选择展平第一个示例,但这不是必需的。
user7116 2011年

2
事实上...但你声称后者远远快。这不是完全清楚,这将是显著都更快-毕竟,这是如何被使用的性能差异的显着性将取决于。
乔恩·斯基特

1
@乔恩:没有分歧。正如您所注意到的,现实可能是LINQ提供程序去对表达式进行了有用的优化转换。但是,鉴于第二个只需要一个循环,并且受益于布尔短路,因此很难理解为什么一般不将其标记为“快得多”。如果OP仅包含5个元素,那么我的意思是没有意义的。
user7116 2011年

11

当我跑步

from c in Customers
where c.CustomerID == 1
where c.CustomerID == 2
where c.CustomerID == 3
select c

from c in Customers
where c.CustomerID == 1 &&
c.CustomerID == 2 &&
c.CustomerID == 3
select c customer table in linqpad

针对我的客户表,它输出相同的SQL查询

-- Region Parameters
DECLARE @p0 Int = 1
DECLARE @p1 Int = 2
DECLARE @p2 Int = 3
-- EndRegion
SELECT [t0].[CustomerID], [t0].[CustomerName]
FROM [Customers] AS [t0]
WHERE ([t0].[CustomerID] = @p0) AND ([t0].[CustomerID] = @p1) AND ([t0].[CustomerID] = @p2)

所以在翻译成sql时没有区别,您已经在其他答案中看到了它们将如何转换为lambda表达式


好的,那么您想说,如果我使用其中任何一种,都不会对性能产生任何影响?
Bimal Das

WHERE子句实际上是链接在一起的。因此,如何编写都无关紧要。没有性能差异。
hastrb

3

看起来,这两个语句将转换为不同的查询表示形式。根据QueryProviderCollection,可能会优化或无法优化。

当这是linq到对象的调用时,多个where子句将导致相互读取的IEnumerable链。使用单句形式将有助于提高性能。

当基础提供程序将其转换为SQL语句时,两个变体都可能会创建相同的语句。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.