2010-11-12 78 views
5

我试图在我链接linq方法时,特别是在多次链接相同方法时,绕过C#编译器执行的操作。了解C#编译器如何处理链接linq方法

简单示例:假设我试图根据两个条件筛选一个整数序列。

最明显的事情是这样的:

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

但我们可以也链条,其中的方法,在每一个条件:

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

我有一个在Reflector中查看IL;这是两种方法明显不同,但进一步分析它超出了我的知识,此刻:)

我想了解一下:
一)编译器在每个实例做什么不同,为什么。
B)是否有任何性能问题(不是要微观优化;!只是好奇)

回答

9

答案(一)是短暂的,但我会去到下面详细:

编译器实际上并没有做的链接 - 它发生在运行时,通过正常对象的组织!这里的魔术远不及乍一看 - Jon Skeet recently completed the "Where clause" step在他的博客系列中,重新实现LINQ to Objects。我建议阅读。

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

当你开始遍历这个WhereEnumerable(例如,在foreach在后面的代码下),在内部它只是开始迭代的IEnumerable有参考。

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

直到我们碰到原点,它实际上是某种数组或真实元素的存储。因为每个Enumerable都会说“OK,这是我的元素”,将它传递回链,它也会应用自己的自定义逻辑。对于Where,它应用lambda来查看元素是否通过条件。如果是这样,它允许它继续到下一个呼叫者。如果失败,则停止在该点处,返回到其引用的Enumerable,并请求下一个元素。

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

要回答(B),有总是有差别,但在这里它太琐碎打扰。不要担心:)

+0

好的答案。我们需要更多像Stackoverflow这样的材料。 – 2010-11-12 03:53:07

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

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