为啥 linq to object 手动实现迭代器?
Posted
技术标签:
【中文标题】为啥 linq to object 手动实现迭代器?【英文标题】:Why is linq to object implementing iterators manually?为什么 linq to object 手动实现迭代器? 【发布时间】:2015-09-12 17:16:51 【问题描述】:在浏览 .net 核心源代码时,我注意到即使是源代码形式的迭代器类也是手动实现的,而不是依赖于 yield 语句和自动 IEnumerable 实现。
您可以在这一行看到例如 where 迭代器 https://github.com/dotnet/corefx/blob/master/src/System.Linq/src/System/Linq/Enumerable.cs#L168 的声明和实现
我假设如果他们经历了这样做而不是简单的 yield 语句的麻烦,那么肯定会有一些好处,但我不能立即看到哪个,这似乎与我记得编译器自动执行的操作非常相似几年前我回顾了 eric lippert 的博客,我记得当我在早期天真地用 yield 语句重新实现 LINQ 以更好地理解它时,性能配置文件类似于 .NET 版本。
这激起了我的好奇心,但这也是一个非常重要的问题,因为我正处于一个相当大的数据中间 - 在内存项目中,如果我遗漏了一些明显可以使这种方法更好的东西,我很想知道权衡.
编辑:澄清一下,我确实理解为什么他们不能只在 where 方法中产生(不同容器类型的不同枚举),我不明白的是为什么他们实现迭代器本身(也就是说,而不是分叉到不同的迭代器,分叉到不同的方法,根据类型进行不同的迭代,并让步以自动实现状态机,而不是手动实现 case 1 goto 2 case 2 等)。
【问题讨论】:
【参考方案1】:一个可能的原因是专用迭代器执行了一些优化,例如组合选择器和谓词以及利用索引集合。
这些的用处在于,可以以比 yield
的编译器魔法生成的更优化的方式迭代某些源。通过创建这些自定义迭代器,他们可以将有关源的额外信息传递给单个链中的后续 LINQ 操作(而不是使该信息仅可用于链中的第一个操作)。因此,所有Where
和Select
操作(它们之间没有其他任何东西)可以作为一个执行。
【讨论】:
如果您检查对我的问题的编辑,您会看到索引集合解释了自定义分叉但不一定自己实现迭代器,我只是惊讶于引入了如此多的编译器魔法来支持 linq 并且'不在实现中使用它 然而github.com/dotnet/corefx/blob/master/src/System.Linq/src/System/… 似乎让你正确地将选择器与 where 结合起来。虽然我不明白为什么必须实现迭代器而不是简单地传递给需要 2 个资金并且也会产生的 whereselect,但我真的不明白为什么他们手动执行迭代器工作 @RonanThibaudau 让我澄清一下。如果对数组执行 LINQ 操作,则可以利用其索引器。但是,如果您对该结果执行另一个操作(并假设结果现在是“通用”迭代器),那么下一个操作将失去利用索引器的能力(因为就第二个操作而言,源现在是一个“通用”可枚举)。通过创建自定义迭代器,他们允许多个 LINQ 操作利用索引器(而不是只允许它用于链中的第一个操作)。现在更有意义了吗? 实际上是的,这是有道理的,如果你只是让出而不是返回迭代器,那么链接到另一个方法将链接到 IEnumerableyield
的编译器魔法并没有被引入来支持 linq,它从 2.0 开始就在 .NET 中,这比 linq 早了一段时间。以上是关于为啥 linq to object 手动实现迭代器?的主要内容,如果未能解决你的问题,请参考以下文章
Linq之旅:Linq入门详解(Linq to Objects)
Linq之旅:Linq入门详解(Linq to Objects)