我一直在使用迭代器,我喜欢它们。
但是虽然我一直在努力思考它,但我无法弄清楚“如何识别迭代器的编译器”。我也研究过它,但找不到任何资源来解释编译器设计环境中的情况。
详细说明,大多数关于迭代器的文章暗示存在某种实现所需行为的“魔法”。他们建议编译器维护一个状态机,以便跟踪执行的位置(可以看到最后一次'yield return')。我对迭代器的这个属性特别感兴趣,可以进行惰性求值。
顺便说一下,我知道什么是状态机,已经采用了编译器设计课程,研究过龙书。但显然,我无法将我所研究的内容与csc的“魔法”联系起来。
感谢任何知识或差异化的想法。
答案 0 :(得分:5)
它比看起来更简单。编译器可以将迭代器函数分解为单个块;块被yield
语句划分。
状态机只需跟踪我们当前所在的块,并在下次调用迭代器时直接跳转到此块。我们还需要跟踪所有局部变量(当然)。
然后,我们需要考虑一些特殊情况,特别是包含yield
s的循环。幸运的是,IL(但不是C#本身)允许goto
将跳转到循环并恢复它们。
请注意,有一些非常复杂的边缘情况,例如C#不允许在yield
块中finally
,因为将函数保留在yield
上是非常困难的(不可能?),然后恢复该函数,执行清理,重新启动 - 抛出任何异常和保留堆栈跟踪。
Eric Lippert发布了in-depth description这个过程。(阅读他所链接的文章!)
答案 1 :(得分:1)
我会尝试的一件事是在C#中编写一个简短的例子,编译它,然后在它上面使用Reflector。我认为这个“yield return”的东西只是语法糖,所以你应该能够看到编译器如何在反汇编程序的输出中处理它。
但是,我对这些事情并不是很了解,所以也许我完全错了。