int[] scores = { 10, 90, 100, 30, 70, 29, 45 };
var highScoresQuery = from score in scores
where score > 80
orderby score descending
select score;
上面这段代码会延迟执行
,如果使用聚合函数,如:.Count() 就会立即执行
。为什么要这么做,好处是什么? 为什么 Count() 不采用延迟执行
呢?
Linq获得的是表达式,表达式只有执行之后才会有结果。
所以.Count()这类是执行表达式的方法。
延迟执行的一个好处就是产生表达式的地方和获得结果的地方不必要在一起。
比如,你可以有一个基础表达式,然后根据不同的条件获得最终的表达式,然后再传给接受表达式的方法,这个方法就可以通过执行表达式来获得最终结果。
Lambda表达式只是语法糖,从动态语言借鉴过来,但作为C#静态语言其本质操作并没有改变。正如楼上所言,你可能在多个地方动态改变了查询语句,但编译器只在你执行最终操作的时候才将Lambda表达试“翻译”为最终执行的C#或SQL代码。我理解Count()和ToList()在语义上来讲就是当下的即时最终操作。
linq表达式返回的是IQueryable
并不是查询结果。延迟执行的目的有几个:
- 为后续动态表达提供可能。比如你上面的表达式,如果想再加个where条件,而且这个条件加不加事先是不知道的,怎么办呢。
- 在设计之初就是为sql服务的,即linq to sql,将linq表达式转化成sql表达式
highScoresQuery
是个IQueryable
,对IQueryable
执行终结方法(比如ToList
,Count
)都将执行表达式遍历、转化、并查询结果。由于Linq的灵活性,派生出了linq的很多其他用法,比如你这个就是Linq to Object。但是不同的linq实现有一些差异,延不延迟不能一概而论,还要看具体实现,但是原则上是会有个表达式遍历和转化的过程的,所以会延迟。
linq相当于一个查询语句的封装,可以理解为是一个“sql语句的构造器”。定义或者修改一个linq的语句,其实就是在构造器上调整构造参数。
ToList、Count之类的函数,可以想象它的操作对象是查询结果,而不是一个查询语句本身。因此,对linq这个构造器执行结果类函数时,构造器就会根据已有的构造参数生成最终的查询语句、执行该语句、返回查询结果,然后才会将这个查询结果交付结果类函数中使用。
在这整个过程中,从开始定义linq,到生成执行最终查询语句并返回结果的这段时间(或者代码空间),就是你说的“延迟”。
linq的优点在于代码及代码逻辑的简洁明快。另外还附带了Lambda表达式的优(gao)雅(bi)性(ge)、推迟sql遍历查询这种高消耗操作带来的总体性能优化等优点。(但需要注意的是,linq本身相对与原生查询代码肯定是性能下降的)