延迟 LINQ 查询执行实际上是如何工作的?

2024-04-08

最近我遇到这样的问题:What numbers will be printed considering the following code:

class Program
{
    static void Main(string[] args)
    {
        int[] numbers = { 1, 3, 5, 7, 9 };
        int threshold = 6;
        var query = from value in numbers where value >= threshold select value;

        threshold = 3;
        var result = query.ToList();

        result.ForEach(Console.WriteLine);
        Console.ReadLine();
    }
}

Answer: 3, 5, 7, 9

这让我很惊讶。我以为threshold值将在查询构造时放入堆栈,稍后在执行时,该数字将被拉回并在条件中使用......但这没有发生。

另一个案例(numbers被设定为null就在执行之前):

    static void Main(string[] args)
    {
        int[] numbers = { 1, 3, 5, 7, 9 };
        int threshold = 6;
        var query = from value in numbers where value >= threshold select value;

        threshold = 3;
        numbers = null;
        var result = query.ToList();
        ...
    }

似乎对查询没有影响。它打印出与前面示例完全相同的答案。

谁能帮助我了解幕后到底发生了什么?为什么要改变threshold更改时对查询执行有影响numbers不是吗?


您的查询可以用方法语法这样编写:

var query = numbers.Where(value => value >= threshold);

Or:

Func<int, bool> predicate = delegate(value) {
    return value >= threshold;
}
IEnumerable<int> query = numbers.Where(predicate);

这些代码段(包括查询语法中您自己的查询)都是等效的。

当您像这样展开查询时,您会看到predicate is an 匿名方法 https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/anonymous-methods and threshold is a closure https://stackoverflow.com/questions/428617/what-are-closures-in-net在那个方法中。这意味着它将采用执行时的值。编译器将生成一个实际的(非匿名)方法来处理这个问题。该方法不会在声明时执行,而是针对每个项目时执行query被枚举(执行是deferred)。由于枚举发生在值之后threshold被改变(并且threshold是一个闭包),使用新值。

当你设置numbers to null,您将引用设置为无处,但该对象仍然存在。这IEnumerable由返回Where(并在中引用query)仍然引用它,并且初始引用是并不重要null now.

这解释了这种行为:numbers and threshold在延迟执行中扮演不同的角色。numbers是对枚举数组的引用,而threshold是一个局部变量,其范围“转发”到匿名方法。

扩展,第 1 部分:枚举期间闭包的修改

当您更换线路时,您可以将示例更进一步......

var result = query.ToList();

...with:

List<int> result = new List<int>();
foreach(int value in query) {
    threshold = 8;
    result.Add(value);
}

你正在做的是改变的价值threshold during数组的迭代。当你第一次点击循环体时(当value是 3),您将阈值更改为 8,这意味着值 5 和 7 将被跳过,下一个要添加到列表中的值是 9。原因是threshold将在每次迭代时再次评估,并且then将使用有效值。由于阈值已更改为 8,因此数字 5 和 7 不再被评估为大于或等于。

扩展,第 2 部分:实体框架的不同

让事情变得更复杂的是,当您使用 LINQ 提供程序创建与原始查询不同的查询然后执行它时,情况会略有不同。最常见的示例是实体框架 (EF) 和 LINQ2SQL(现已基本被 EF 取代)。这些提供程序根据原始查询创建 SQL 查询枚举之前。由于这次闭包的值只被计算一次(它实际上不是一个闭包,因为编译器生成一个表达式树而不是一个匿名方法),所以变化threshold枚举期间对结果没有影响。这些更改发生在查询提交到数据库之后。

从中得到的教训是,您必须始终了解您正在使用哪种 LINQ 风格,并且了解其内部工作原理是一个优势。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

延迟 LINQ 查询执行实际上是如何工作的? 的相关文章

随机推荐

  • 检查 JSON 对象中的值

    我需要查明 id 是否出现在我的 JSON 对象中 例如 requested 2009 07 25T14 12 25 01 00 channels id 1 name General created 2009 07 25 14 00 02
  • 声明静态成员的类装饰器(例如,对于 log4net)?

    我正在使用 log4net 我们的代码中有很多这样的内容 public class Foo private static readonly ILog log LogManager GetLogger typeof Foo 一个缺点是 这意味
  • 如何使用oauth2访问StackExchange API?

    我正在按照此处提到的说明进行操作 https api stackexchange com docs authentication https api stackexchange com docs authentication 但由于没有提供
  • KnockoutJS - 打印迭代索引作为输入名称

    我正在尝试结合 Spring MVC 创建我的第一个 KnockoutJS 表单视图 ModelAttribute捆绑 数据通过 Ajax 加载并使用 KnockoutJS 填充 通过 KnockoutJS 添加数据 通过 Ajax 和 K
  • 如何在 Matplotlib 中的 x 轴上分配相等的缩放比例?

    我目前拥有的是这样的 x 3 0 4 0 5 0 5 0 6 0 7 0 9 0 9 0 9 0 11 0 y 6 0 5 0 4 0 2 5 3 0 2 0 1 0 2 0 2 5 2 5 生成以下图表 我想要的是在我的轴上具有相同的缩放
  • 错误:无法使用反射定义类

    我正在用这个制作应用程序webpage https hackernoon com a guide to tdd a react redux todolist app part 1 b8a200bb7091 我尝试测试 e2e tests 但
  • 有没有便宜或免费的 VB6 编程 IDE? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 为什么 NSNumber 是不可变的?

    为什么 NSNumber 是不可变的 有充分的理由吗 因为现在我正在考虑创建自己的类只是为了可变性 一个数字是一个very基本数据类型 数字就是一个数字 如果你改变它 它就会变成别的东西 数字根本无法改变 与更复杂的数据相比 对象本身仍然代
  • WPF RadioButton InverseBooleanConverter 不工作

    我有两个 RadioButtons 我将它们绑定到 ViewModel 中的布尔属性 不幸的是 我在转换器中收到错误 因为 targetType 参数为空 现在我并不期望 targetType 参数为空 我期望 True 或 False 但
  • 仅将唯一项目添加到列表中

    当远程设备通过网络宣布自己时 我将其添加到列表中 我只想将之前未添加过的设备添加到列表中 这些公告是通过异步套接字侦听器发出的 因此添加设备的代码可以在多个线程上运行 我不确定我做错了什么 但无论我尝试什么 最终都会出现重复 这是我目前拥有
  • 开源项目如何实现有效的民主治理? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 如何成功实施民主 非BDFL http en wikipedia org wiki Benevolent Dictator For L
  • Node.JS PM2 内存泄漏

    我正在运行我的服务器pm2 start and pm2 monit正在向我展示3GB memory2个小时之后 所以我附加了memwatch 现在我又等了2个小时 再次显示内存pm2 monit到达3GB 因此 我检查了 memwatch
  • Gitlab - Xcode 无法连接远程存储库

    当我通过 Xcode 连接我的存储库时遇到问题 我在远程服务器上的 TurnkeyLinux 虚拟设备上安装了 Gitlab 版本 完全预装 在 Gitlab Web 界面中 我创建了一个新的测试用户 testuser 密码为 passwo
  • 通过“getDisplayMedia”检查浏览器/平台是否支持屏幕捕获

    我们可以通过以下方式请求媒体流到屏幕或窗口navigator mediaDevices getDisplayMedia 然而 这会立即提示用户决定使用哪种捕获 我需要检查浏览器 平台是否支持屏幕捕获 当然 也可以检查一下 getDispla
  • 如何判断时区是否在一年中的任何时间遵守夏令时?

    在 PHP 中 您可以使用如下方法判断给定日期是否在夏令时期间 isDST date I myDate 1 or 0 问题是 这只能告诉您该时间点是否处于夏令时 有没有可靠的方法来检查 DST 在该时区的任何时间是否生效 编辑以澄清 澳大利
  • 使用 Silverlight 的多个屏幕/监视器

    我想编写一个 Silverlight 应用程序 它有 2 个或更多可以 交互 的浏览器窗口 一个示例是父 详细信息显示 其中选择 父 列表 在一个浏览器窗口中 中的项目将在另一个窗口中显示该项目的详细信息 要点是 在多显示器设置中 用户可以
  • Django-manage.py sql APPNAME 不生成模型 SQL

    我正在开发一个相对较大的平面应用程序 为了保持关注点分离 我将模型和视图文件分成auth models dashboard models taxonomy models和更多 这些已放置在文件夹结构中 如下所示 APPNAME app mo
  • 为什么在循环外部和内部声明具有相同名称的变量不会产生错误?

    int i for i 0 i lt 5 i int i 10 printf d i 我有两个问题 为什么没有重新声明错误i 为什么输出会是105次和没有10 1 time 这一切都与scope的标识符 标识符只是 C 中赋予实体 对象 函
  • 单个 Blazor 项目可以同时包含 WebAssembly 部分和服务器端部分吗? [复制]

    这个问题在这里已经有答案了 现在 Blazor WebAssembly 已正式发布 我尝试使用该模板创建我的第一个 Blazor wasm 项目 但很快发现有一点与我的预期完全不同 模板中似乎没有 服务器 部分 并且发布的结果仅包含静态资产
  • 延迟 LINQ 查询执行实际上是如何工作的?

    最近我遇到这样的问题 What numbers will be printed considering the following code class Program static void Main string args int nu