• Linq基础知识之延迟执行


    Linq中的绝大多数查询运算符都有延迟执行的特性,查询并不是在查询创建的时候执行,而是在遍历的时候执行,也就是在enumerator的MoveNext()方法被调用的时候执行,大说数Linq查询操作实例方法返回的都是IEnumerable<T>,所以只有在使用foreach遍历的时候,查询方法才能被真正的执行.请参考C# 通过IEnumberable接口和IEnumerator接口实现自定义集合类型foreach功能

    示例代码如下:

    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就会取代上面的整个对象模型。

  • 相关阅读:
    python学习笔记1--datetime的使用
    python学习笔记2--子类父类继承时的参数传递
    python学习笔记1--错误,异常,调试
    JS同异步编程
    AMD /CMD
    i++ && ++i
    将url问号后面的参数变成对象
    字符串的常用方法
    函数 && 函数运行机制
    Math数学函数及常用方法
  • 原文地址:https://www.cnblogs.com/GreenLeaves/p/7601476.html
Copyright © 2020-2023  润新知