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

我正在尝试围绕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 recently completed the “Where clause” step在他的博客系列中重新实现LINQ to Objects.我建议你仔细阅读.

在很短的时间内,会发生这样的情况:每次调用Where扩展方法时,它都会返回一个新的WhereEnumerable对象,它有两个东西 – 对前一个IEnumerable的引用(你调用的那个),以及你提供的lambda .

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

“This foreach just asked me for the next element in my sequence,so I’m turning around and asking you for the next element in your sequence”.

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

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

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

相关文章

在要实现单例模式的类当中添加如下代码:实例化的时候:frmC...
1、如果制作圆角窗体,窗体先继承DOTNETBAR的:public parti...
根据网上资料,自己很粗略的实现了一个winform搜索提示,但是...
近期在做DSOFramer这个控件,打算自己弄一个自定义控件来封装...
今天玩了一把WMI,查询了一下电脑的硬件信息,感觉很多代码都...
最近在研究WinWordControl这个控件,因为上级要求在系统里,...