C#_深入理解Linq的延迟执行
2018-03-27 14:14
330 查看
一、Linq简介
Linq的查询三部曲:一、获取数据源;二、创建查询;三、执行查询。二、延迟查询带来的好处
延迟查询有什么好处呢?请看下面一段代码: private static void Main(){
var list = new List<int>();
var rd = new Random();
for (var i = 0; i < 100000000; i++)
{
var num = rd.Next(-10000000, 10000000);
list.Add(num);
}
RecordingTime(list);
}
private static void RecordingTime(IEnumerable<int> list) { var watch = new Stopwatch(); watch.Start(); //开始计时 var nums1 = list.Where(n => n > 0); var nums2 = new List<int>(); foreach (var i in list) { if (i > 0) { nums2.Add(i); } } watch.Stop(); //停止计时 var time = watch.ElapsedMilliseconds; Console.WriteLine($"耗时:{time}毫秒"); }大家可以对比一下num1和num2的运行效率,一亿条数据中,使用Linq求集合中的正数耗时仅为2毫秒(可能会有十几毫秒的误差);而使用普通方法(遍历加判断)耗时却达到了2秒多。效率提升近2000倍!
Linq的效率为什么会这么快呢?
这是因为Linq在上面的代码中仅仅做了两个工作:1、获取数据源,2、创建查询;而使用普通方法不光做了这两个工作,它还多做了执行查询工作,因此Linq在此处运行效率奇高。
三、延迟执行会有什么影响
用代码说话var list = new List<int> { 1, 2, 3, 4, 5, 6, 7 }; var enumList = list.Where(x => x > -4); foreach (var num in enumList) { Console.WriteLine(num); } Console.WriteLine("■■■■■■■■■■■■"); list.Add(-1); foreach (var num in enumList) { Console.WriteLine(num); }此处我上下遍历的enumList是同一个集合,尽管我在遍历完第一次的时候改变了List,正常来讲我上下两次遍历输出的内容应该是一致的,然而结果却不尽然。
刚刚提到Linq仅仅做了两部工作:1、获取数据源,2、创建查询。那么执行查询是在何时何处被执行的呢?答案就是:当enumList被用到的时候才会去执行,也就是说这段代码中,var enumList = list.Where(x => x > -4);这行代码被执行了两次。第一次执行的时候是第一次遍历enumList的时候,第二次执行的时候是第二次遍历enumList的时候(大家可以打断点观察一下)。
四、延迟执行的原理
继续上代码private static void Main() { var list = new List<int> { -1, 1, 2, 3, 4, 5 }; var list1 = Delay(list); foreach (var num in list1) { Console.WriteLine(num); } list.Add(-3); list.Add(7); Console.WriteLine("■■■■■■■■■■■■"); foreach (var num in list1) { Console.WriteLine(num); } }
private static IEnumerable<int> Delay(IEnumerable<int> list) { foreach (var num in list) { if (num>0) { yield return num; } } }其实Linq的延迟执行原理很简单,如果你理解yield return,那么你就会明白这两者其实原理是一模一样的。
可以发现这段代码跟上面的那段代码运行模式是一样的,当你将断点打在var list1 = Delay(list);时,单步调试是不会进入Delay方法的,仅当你去遍历list1的时候它才会去执行Delay方法,而且是你用几次list1,它就会去执行几次Delay方法。yield return与return不同之处就在于它是“按需供给”,即你什么时候用它就什么时候(执行)返回给你值。
之前说到延迟执行可能会影响结果,那么当我多次使用list1时,Delay这个方法就会多次执行,程序的运行效率是否会非常低呢?看一下我的测试,一万个整形数据打印七次。 yield return(模拟延迟执行):
直接打印:
普通打印的运行时间甚至更低(因为普通打印是打印所有数字,Delay方法过滤掉负数了)!由此,可以看出:当延迟执行的结果被多次调用时,并不会影响运行效率。
五、哪些Linq查询操作符是延迟执行的
转到Linq查询操作符的定义看一下当返回值为IEnumerable<TSource>、IEnumerable<IGrouping<TKey, TSource>>和IOrderedEnumerable<TSource>的时候,Linq为延迟执行。实际上上述三个返回值类型都实现了IEnumerable<T>(公开枚举数)这个接口,yield return的返回值就是IEnumerable<T>,所以,当Linq查询操作符的返回值为上述三个类型时,查询为延迟查询,其他为立即执行。
六、如何避免延迟执行
可以看到延迟执行既有好处又有一定的影响,当延迟执行会对自己的程序产生影响时,如何去避免这个影响呢。很简单,将返回值转换为另外一种类型即可,Linq的查询操作符中包含了转换操作的定义:ToArray(转换为数组)、ToDictionary(转换为字典)、ToList(转换为集合),使用这些转换运算就可以将延迟执行转换为立即执行,也就可以避免延迟执行带来的影响了。本人的第一篇博文,初学C#不到3个月,写得不好的地方请在评论区指出,我会及时修正。
相关文章推荐
- C#中Linq延迟执行问题
- 深入理解C# 3.0的五项主要改进 (LINQ)
- 深入理解 c# 第一章 用LINQ 查询 xml 文件
- C#使用LINQ中Enumerable类方法的延迟与立即执行的控制
- 深入理解 c# 第二章 数组协变以及执行时类型检查
- 深入理解 c# 第一章 使用LINQ 查询表达式
- 深入理解 c# 第五章 代码精简的极端例子 求平方根
- <<深入理解mariadb和mysql>>之mysql执行计划分析学习记录
- 深入理解利用new创建对象的执行过程以Person p=new Person("张三",20);为例
- [C# 基础知识系列]专题七: 泛型深入理解(一)
- 深入理解计算机原理——程序与执行(二)
- 深入理解Hibernate的延迟加载机制
- [深入理解Java虚拟机]第八章 字节码执行引擎-方法调用
- 深入理解C#第二版笔记
- C#接口作用的深入理解
- LINQ之路 6:延迟执行(转载)
- 深入理解单表执行计划
- [原创]深入理解C# 3.x的新特性(3):从Delegate、Anonymous Method到Lambda Expression
- 深入理解javascript原型和闭包(8)——简述【执行上下文】上
- 深入理解静态链表(,物理上是一个结构体数组,逻辑上是两个链表,)执行一下,,,