• LINQ中的延迟执行


    1.前言

    在使用 LINQ 查询的过程中存在着两种查询方式,一种是立即执行,另一种是延迟执行。下面将主要讲解 LINQ 的特殊支持——延迟执行。

    2.延迟执行

    延迟执行意味着,他们不是在查询创建的时候执行,而是在使用 foreach 语句遍历的时候执行(换句话说,当 GetEnumerator 的 MoveNext 方法被调用时)。现在考虑下面这种查询的实现:

    static void Main(string[] args)
    {
        List<int> list = new List<int>();
        list.Add(1);
        IEnumerable<int> result =
            from item in list
            select item;
        list.Add(2);
        foreach(var item in result)
        {
            Console.WriteLine(item);
        }
        Console.ReadKey();
    }

    其输出结果为:

    1
    2

    可以发现,在定义了 LINQ 查询后再向 list 中添加新项 “2”,仍然可以在 froeach 语句中输出 ”2“, 这是因为直到f oreach 语句对 result 进行遍历时,LINQ查询才会执行,这便是 LINQ 的延迟执行。

    除了下面两种查询运算符,所有其他的运算符都是延迟执行的:

    • 返回单个元素或者标量值的查询运算符,如First、Count等。
    • 下面这些转换运算符:ToArray、ToList、ToDictionary、ToLookup。

    上面两种运算符会被立即执行,因为他们的返回值类型没有提供延迟执行的机制,比如下面的查询会被立即执行。

    int matches = list.Where(n => (n % 2) == 0).Count();     // 1

    将返回的集合对象分配给一个新变量时,实现的时直接执行,如下面的示例:

    static void Main(string[] args)
    {
        List<int> list = new List<int>() { 1, 2, 3, 4, 5 };
        IEnumerable<int> result =
            from item in list
            select item;
        List<int> newlist = list.ToList();
        list.Add(6);
        foreach (var item in newlist)
        {
            Console.Write(item + " ");
        }
        Console.ReadKey();
    }

    其输出结果如下所示,即使在 ToList 后更改 list 中的项,foreach 语句的执行结果依旧不变:

    1 2 3 4 5

    3.重复执行

    但是,延迟执行带来的一个影响是,当我们重复遍历查询结果时,查询会被重复执行,例如下面的示例:

    static void Main(string[] args)
    {
        var numbers = new List<int>() { 1, 2 };
        IEnumerable<int> query = numbers.Select(n => n * 10);   // Build query
        foreach (int n in query) Console.Write(n + " ");        // 10 20
        numbers.Clear();
        foreach (int n in query) Console.Write(n + " ");        // <nothing>
        Console.ReadKey();
    }

    显然之前的查询数据会被覆盖,当我们需要保留之前查询结果时,这显然是不满足要求的,这时候可以使用上文中提到的转换运算符 ToArray、ToList、ToDictionary、ToLookup 方法存储查询结果。

    4.变量捕获

    延迟执行还有一个不好的副作用。如果查询的lambda表达式引用了程序的局部变量时,查询会在执行时对变量进行捕获。这意味着,如果在查询定义之后改变了该变量的值,那么查询结果也会随之改变。

    IEnumerable<char> query = "How are you, friend.";
    foreach (char vowel in "aeiou")
        query = query.Where(c => c != vowel);
    foreach (char c in query) Console.Write(c); //How are yo, friend.

    结果 query 中只有"u"被过滤了,可以修改代码如下:

    IEnumerable<char> query = "How are you, friend.";
    foreach (char vowel in "aeiou")
    {
        char temp = vowel;
        query = query.Where(c => c != temp);
    }
    foreach (char c in query) Console.Write(c); //Hw r y, frnd.

    此时,才能实现对元音字母的过滤。

  • 相关阅读:
    四月⑨日
    4月⑦日
    4月6日
    四月⑧日
    第一天上课
    modelsim的do文件
    SCCB协议
    FPGA之VGA控制
    FPGA常用术语
    乘法器之六(硬件乘法器)
  • 原文地址:https://www.cnblogs.com/jizhiqiliao/p/9850178.html
Copyright © 2020-2023  润新知