c# - 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,的值为b
10。
让我意识到这可能/将会发生的原始代码涉及 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()
后面的代码行调用了 中的代码?
解决方案
您有一个对 LINQ 查询的引用,该查询的评估次数与您迭代它们的次数一样多。
从文档中(您可以看到这称为Deferred Execution):
如前所述,查询变量本身只存储查询命令。查询的实际执行被推迟,直到您在 foreach 语句中迭代查询变量。这个概念被称为延迟执行
...
因为查询变量本身从不保存查询结果,所以您可以随意执行它。例如,您可能有一个由单独的应用程序不断更新的数据库。在您的应用程序中,您可以创建一个检索最新数据的查询,并且您可以在某个时间间隔重复执行它以每次检索不同的结果。
所以,当你有
IEnumerable<int> result = numbers.Select(DoSomething);
您有一个查询的引用,该查询会将每个元素转换numbers
为DoSomething
.
所以,你可以这样说:
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
推荐阅读
- html - 如何在鼠标悬停时更改垫表的字体颜色
- python - 使用 Docker API for Python 时出现“URL 超出最大重试次数:/v1.35/containers/create”错误
- react-native - 如何使用 react-native-svg 和 Animated 为 gradientTransform 属性设置动画
- rust - 我如何找出命名生命周期的来源?
- maven - 如何为 Maven 配置 client-maven-plugin?
- c - Mellanox libvma:如何接收与用户空间时间相当的数据包时间戳?
- android - Android 中的 Amazon Cognito 用户池和 Facebook 登录
- javascript - 如何使用 Ajax 在 Flask 中显示返回的输入?
- python - tkinter 中的命令功能无法正常工作
- python - pandas groupby - 组名而不是数字