繁体   English   中英

了解C#编译器如何处理链接linq方法

[英]Understanding how the C# compiler deals with chaining linq methods

我正在尝试围绕C#编译器在链接linq方法时所做的事情,特别是在多次链接同一方法时。

简单的例子:假设我试图根据两个条件过滤一系列int。

最明显的事情是这样的:

IEnumerable<int> Method1(IEnumerable<int> input)
{
    return input.Where(i => i % 3 == 0 && i % 5 == 0);
}

但我们可以链接where方法,每个方法都有一个条件:

IEnumerable<int> Method2(IEnumerable<int> input)
{
    return input.Where(i => i % 3 == 0).Where(i => i % 5 == 0);
}

我看了反射器中的IL; 这两种方法明显不同,但目前我不知道进一步分析它:)

我想找出:
a)编译器在每个实例中做的不同,以及为什么。
b)是否有任何性能影响(不是试图微观优化;只是好奇!)

(a)的答案很简短,但我将在下面详细介绍:

编译器实际上并不进行链接 - 它通过对象的正常组织在运行时发生! 这里的魔法远不如乍看之下 - Jon Skeet 最近完成了他的博客系列中的“Where子句”步骤 ,重新实现LINQ to Objects。 我建议你仔细阅读。

在很短的方面,会发生什么情况是这样的:每次调用Where扩展方法,它返回一个新WhereEnumerable有两件事情的对象-以前的一个参考IEnumerable (你叫的一个Where ),并且您提供的拉姆达。

当你开始迭代这个WhereEnumerable (例如,在你的代码中的foreach中),在内部它只是开始迭代引用的IEnumerable

“这foreach刚才问我在我的序列中的下一个元素,所以我转身问你为你的序列中的下一个元素”。

这一直沿着链条向下直到我们击中原点,这实际上是某种阵列或真实元素的存储。 当每个Enumerable然后说“OK,这是我的元素”将它传递回链时,它也应用它自己的自定义逻辑。 对于Where ,它应用lambda来查看元素是否通过了条件。 如果是这样,它允许它继续到下一个调用者。 如果失败,则在该点停止,返回其引用的Enumerable,并请求下一个元素。

这种情况一直持续到每个人的MoveNext返回false,这意味着枚举已经完成,并且没有更多的元素。

要回答(b)总会有不同之处,但是这里太麻烦了。 别担心:)

  1. 第一个将使用一个迭代器,第二个将使用两个。 也就是说,第一个建立一个阶段的管道,第二阶段将涉及两个阶段。

  2. 两个迭代器对一个迭代器有轻微的性能劣势。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM