Linq基础知识之延迟执行
2017-09-27 14:06
176 查看
Linq中的绝大多数查询运算符都有延迟执行的特性,查询并不是在查询创建的时候执行,而是在遍历的时候执行,也就是在enumerator的MoveNext()方法被调用的时候执行,大说数Linq查询操作实例方法返回的都是IEnumerable<T>,所以只有在使用foreach遍历的时候,查询方法才能被真正的执行.请参考C# 通过IEnumberable接口和IEnumerator接口实现自定义集合类型foreach功能
示例代码如下:
输出结果一目了然,当创建完查询之后添加的元素也包含到了结果集中,说明查询并没有立即执行,而是在使用foreach遍历之后才执行,这种特性就是Linq的延迟执行.
不止Where查询操作符是这样的,其他的只要返回的是IEnumerable<T>对象的都有延迟执行特性.
注:其他的一些像First、Count、ToArray、ToList、ToDictionary、ToLookup这些都是立即执行的.
当然,对于Linq来说,延迟执行是非常重要的,因为它把查询的创建和查询的执行解耦了,这让我们可以像创建SQL查询那样,分成多个步骤来创建我们的LINQ查询。
重复执行
使用导致延迟执行的查询操作符进行查询操作,并且两次或者两次以上的使用foreach,会导致查询重复执行,重复执行在以下两种情况下,绝对是不好的:
1、当需要在一个确定点保存查询的结果时,因为延迟执行并不会在创建查询之后马上得到查询结果集,所以必须使用上面提到的ToArray、ToList等方法使查询立即执行得到结果集并进行存储,代码如下:
例子不是贴切,但是意思到了,此时的查询是立即执行.
2、有些查询比较耗时,比如对一个非常大的数据集进行操作或者通过Linq远程操作数据库操作数据时,这个时候的重复执行会严重影响性能.
和传统的集合类型如array,linked list不同,一个装饰者sequence并没有自己用来存放元素的底层结构,而是包装了我们在运行时提供的另外一个sequence。此后当我们从装饰者sequence中请求数据时,它就会转而从包装的sequence中请求数据。
比如调用Where会创建一个装饰者sequence,其中保存了输入sequence的引用、lambda表达式还有其他提供的参数。下面的查询对应的装饰者sequence如图所示:
当我们遍历lessThanTen时,实际上我们是在通过Where装饰者从Array中查找数据。
而查询运算符链接创建了一个多层的装饰者,每个查询运算符都会实例化一个装饰者来包装前一个sequence,比如下面的query和对应的多层装饰者sequence:
在我们遍历query时,我们其实是在通过一个装饰者链来查询最初的array。
需要注意的是,如果在上面的查询后面加上一个转换运算符如ToList,那么query会被立即执行,这样,单个list就会取代上面的整个对象模型。
示例代码如下:
List<int> list=new List<int>(); list.AddRange(new int[]{ 1, 43, 5, 7, 8 }); IEnumerable<int> result = list.Where(n => n >= 40); list.Add(50); foreach (var n in result) { Console.WriteLine(n); }
输出结果一目了然,当创建完查询之后添加的元素也包含到了结果集中,说明查询并没有立即执行,而是在使用foreach遍历之后才执行,这种特性就是Linq的延迟执行.
不止Where查询操作符是这样的,其他的只要返回的是IEnumerable<T>对象的都有延迟执行特性.
注:其他的一些像First、Count、ToArray、ToList、ToDictionary、ToLookup这些都是立即执行的.
当然,对于Linq来说,延迟执行是非常重要的,因为它把查询的创建和查询的执行解耦了,这让我们可以像创建SQL查询那样,分成多个步骤来创建我们的LINQ查询。
重复执行
使用导致延迟执行的查询操作符进行查询操作,并且两次或者两次以上的使用foreach,会导致查询重复执行,重复执行在以下两种情况下,绝对是不好的:
1、当需要在一个确定点保存查询的结果时,因为延迟执行并不会在创建查询之后马上得到查询结果集,所以必须使用上面提到的ToArray、ToList等方法使查询立即执行得到结果集并进行存储,代码如下:
List<int> list=new List<int>(); list.AddRange(new int[]{ 1, 43, 5, 7, 8 }); IEnumerable<int> result = list.Where(n => n >= 40).ToList(); list.Add(50); foreach (var n in result) { Console.WriteLine(n); }
例子不是贴切,但是意思到了,此时的查询是立即执行.
2、有些查询比较耗时,比如对一个非常大的数据集进行操作或者通过Linq远程操作数据库操作数据时,这个时候的重复执行会严重影响性能.
延迟执行的实现原理
查询运算符通过返回装饰者sequence(decorator sequence)来支持延迟执行。和传统的集合类型如array,linked list不同,一个装饰者sequence并没有自己用来存放元素的底层结构,而是包装了我们在运行时提供的另外一个sequence。此后当我们从装饰者sequence中请求数据时,它就会转而从包装的sequence中请求数据。
比如调用Where会创建一个装饰者sequence,其中保存了输入sequence的引用、lambda表达式还有其他提供的参数。下面的查询对应的装饰者sequence如图所示:
IEnumerable<int> lessThanTen = new int[] { 5, 12, 3 }.Where(n => n < 10);
当我们遍历lessThanTen时,实际上我们是在通过Where装饰者从Array中查找数据。
而查询运算符链接创建了一个多层的装饰者,每个查询运算符都会实例化一个装饰者来包装前一个sequence,比如下面的query和对应的多层装饰者sequence:
IEnumerable<int> query = new int[] { 5, 12, 3 } .Where(n => n < 10) .OrderBy(n => n) .Select(n => n * 10);
在我们遍历query时,我们其实是在通过一个装饰者链来查询最初的array。
需要注意的是,如果在上面的查询后面加上一个转换运算符如ToList,那么query会被立即执行,这样,单个list就会取代上面的整个对象模型。
相关文章推荐
- LINQ之路 6:延迟执行(转载)
- LINQ 的查询执行何时是延迟执行,何时是立即执行,以及查询的复用
- 步步为营VS 2008 + .NET 3.5(11) - DLINQ(LINQ to SQL)之大数据量分页、延迟执行和日志记录
- [C# 基础知识系列]专题十六:Linq介绍
- Linux基础知识[2]【延迟及定时机制】
- android基础知识----延迟和计时效果(handler 、Thread、CountDownTimer )
- php基础知识 5.流程控制 和格式化日期时间 和终止程序执行
- LINQ 的查询执行何时是延迟执行,何时是立即执行,以及查询的复用
- C#基础语言知识--编译和执行过程(一)
- 步步为营VS 2008 + .NET 3.5(11) - DLINQ(LINQ to SQL)之大数据量分页、延迟执行和日志记录
- 保存的js无法执行的解决办法[原创]_基础知识_脚本之家
- Linq延迟执行
- C#使用LINQ中Enumerable类方法的延迟与立即执行的控制
- Linq基础知识小记一
- ADO基础知识::使用Connection对象执行SQL语言,获取查询的结果
- LINQ之延迟执行标准查询操作符(上)
- linq的延迟执行--学习linq的资料和笔记(四)
- shell 脚本执行和基础知识
- C#基础知识复习1代码规范-执行流程(c#)-面向对象-引用命名空间-封装-继承-访问修饰符-虚方法-静态成员-多态-抽象类等
- Linq一 基础知识