Mat*_*ing 7 c# linq cil method-chaining
我正在尝试围绕C#编译器在链接linq方法时所做的事情,特别是在多次链接同一方法时.
简单的例子:假设我试图根据两个条件过滤一系列int.
最明显的事情是这样的:
IEnumerable<int> Method1(IEnumerable<int> input)
{
return input.Where(i => i % 3 == 0 && i % 5 == 0);
}
Run Code Online (Sandbox Code Playgroud)
但我们也可以链接where方法,每个方法都有一个条件:
IEnumerable<int> Method2(IEnumerable<int> input)
{
return input.Where(i => i % 3 == 0).Where(i => i % 5 == 0);
}
Run Code Online (Sandbox Code Playgroud)
我看了反射器中的IL; 这两种方法明显不同,但目前我不知道进一步分析它:)
我想找出:
a)编译器在每个实例中做什么不同,以及为什么.
b)是否有任何性能影响(不是试图微观优化;只是好奇!)
(a)的答案很简短,但我将在下面详细介绍:
编译器实际上并不进行链接 - 它通过对象的正常组织在运行时发生!还有比乍看之下可能会出现什么这里远不如魔术-乔恩斯基特最近完成了"Where子句"步在他的博客系列,重新实现LINQ到对象.我建议你仔细阅读.
在很短的方面,会发生什么情况是这样的:每次调用时Where扩展方法,它返回一个新的WhereEnumerable有两件事情的对象-以前的一个参考IEnumerable(你叫的一个Where),并且您提供的拉姆达.
当你开始遍历这个WhereEnumerable(例如,在foreach后面的代码中下),在内部它只是开始遍历的IEnumerable是它已经引用.
"这
foreach只是问我在我的序列中的下一个元素,所以我转身问您的下一个元素的序列".
这一直沿着链条向下直到我们击中原点,这实际上是某种阵列或真实元素的存储.当每个Enumerable然后说"OK,这是我的元素"将它传递回链时,它也应用它自己的自定义逻辑.对于a Where,它应用lambda来查看元素是否通过了条件.如果是这样,它允许它继续到下一个调用者.如果失败,则在该点停止,返回其引用的Enumerable,并请求下一个元素.
这种情况一直持续到每个人都MoveNext返回false,这意味着枚举已经完成,并且没有更多的元素.
要回答(b),总会有不同之处,但是这里太麻烦了.别担心:)