首页 > 解决方案 > IEnumerable存储稍后调用的函数?

问题描述

我最近遇到了一些不符合我预期的代码。

1: int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8 };
2: IEnumerable<int> result = numbers.Select(n => n % 2 == 0 ? n : 0);
3: 
4: int a = result.ElementAt(0);
5: numbers[0] = 10;
6: int b = result.ElementAt(0);

当我使用 Visual Studio 单步执行这段代码时,我惊讶地发现黄色突出显示从第 4 行跳回到第 2 行的 lambda 表达式,然后又从第 6 行跳到第 2 行的 lambda。

而且,a运行这段代码后的值为0,的值为b10。

让我意识到这可能/将会发生的原始代码涉及 IEnumerable 中的方法调用Select(),并且访问 IEnumerable 的任何属性或特定元素会导致该方法Select()被一次又一次地调用。

// The following code prints out:
// Doing something... 1
// Doing something... 5
// Doing something... 1
// Doing something... 2
// Doing something... 3
// Doing something... 4
// Doing something... 5

using System;
using System.Linq;
using System.Collections.Generic;

class Program
{
    static void Main(string[] args)
    {
        int[] numbers = { 1, 2, 3, 4, 5 };
        IEnumerable<int> result = numbers.Select(DoSomething);

        int a = result.ElementAt(0);
        int b = result.ElementAt(4);
        int c = result.Count();
    }

    static int DoSomething(int x)
    {
        Console.WriteLine("Doing something... " + x);
        return x;
    }
}

我觉得我现在了解代码的行为方式(并且我在网上发现了其他问题,这些问题是这种行为的结果)。但是,究竟是什么原因导致Select()后面的代码行调用了 中的代码?

标签: c#linqselectienumerablefunc

解决方案


您有一个对 LINQ 查询的引用,该查询的评估次数与您迭代它们的次数一样多。

从文档中(您可以看到这称为Deferred Execution):

如前所述,查询变量本身只存储查询命令。查询的实际执行被推迟,直到您在 foreach 语句中迭代查询变量。这个概念被称为延迟执行

...

因为查询变量本身从不保存查询结果,所以您可以随意执行它。例如,您可能有一个由单独的应用程序不断更新的数据库。在您的应用程序中,您可以创建一个检索最新数据的查询,并且您可以在某个时间间隔重复执行它以每次检索不同的结果。

所以,当你有

IEnumerable<int> result = numbers.Select(DoSomething);

您有一个查询的引用,该查询会将每个元素转换numbersDoSomething.
所以,你可以这样说:

int a = result.ElementAt(0);

迭代result直到第一个元素。也是ElementAt(4)如此,但这次它会迭代到第五个元素。请注意,您只看到打印Doing something... 5,因为.Current被评估了一次。如果此时查询无法生成 5 个项目,则调用将失败。
.Count调用再次迭代result查询并返回当时的元素数量。

如果不是保留对查询的引用,而是保留对结果的引用,即:

IEnumerable<int> result = numbers.Select(DoSomething).ToArray();
// or
IEnumerable<int> result = numbers.Select(DoSomething).ToList();

你只会看到这个输出:

// Doing something... 1
// Doing something... 2
// Doing something... 3
// Doing something... 4
// Doing something... 5

推荐阅读