Tej*_*ejs 6 c# linq ienumerable reflector lazy-evaluation
所以,这个问题刚刚被问到:
我的示例代码:
public static void Main(string[] args)
{
foreach (var item in Numbers().Take(10))
Console.WriteLine(item);
Console.ReadKey();
}
public static IEnumerable<int> Numbers()
{
int x = 0;
while (true)
yield return x++;
}
Run Code Online (Sandbox Code Playgroud)
有人可以解释为什么这是懒惰的评估?我在Reflector中查找了这段代码,我比开始时更困惑.
反射器输出:
public static IEnumerable<int> Numbers()
{
return new <Numbers>d__0(-2);
}
Run Code Online (Sandbox Code Playgroud)
对于numbers方法,看起来为该表达式生成了一个新类型:
[DebuggerHidden]
public <Numbers>d__0(int <>1__state)
{
this.<>1__state = <>1__state;
this.<>l__initialThreadId = Thread.CurrentThread.ManagedThreadId;
}
Run Code Online (Sandbox Code Playgroud)
这对我来说毫无意义.我会认为这是一个无限循环,直到我把这些代码放在一起并自己执行.
编辑:所以我现在明白了.取()可以告诉大家,枚举了"结束"在foreach,当它真的没有,但不应号()被调用,在它的全部链接着在起飞前() ?Take结果是实际被枚举的内容,对吗?但是,如果没有对Numbers进行全面评估,那么如何执行?
EDIT2:这只是'yield'关键字强制执行的特定编译器技巧吗?
这不是无限循环的原因是您根据 Linq 的 Take(10) 调用的使用仅枚举了 10 次。现在,如果您编写如下代码:
foreach (var item in Numbers())
{
}
Run Code Online (Sandbox Code Playgroud)
现在这是一个无限循环,因为您的枚举器将始终返回一个新值。C# 编译器获取此代码并将其转换为状态机。如果您的枚举器没有保护子句来中断执行,那么调用者必须在您的示例中这样做。
代码惰性的原因也是代码有效的原因。本质上,Take 返回第一个项目,然后应用程序使用它,然后再获取另一个项目,直到获取 10 个项目。
编辑
这其实和take的加法没有关系。这些称为迭代器。C# 编译器对您的代码执行复杂的转换,从您的方法中创建一个枚举器。我建议阅读它,但基本上(这可能不是 100% 准确),您的代码将进入 Numbers 方法,您可以将其想象为初始化状态机。
一旦你的代码达到了yield返回,你本质上是在说Numbers()停止执行,给他们返回这个结果,然后当他们请求下一个项目时,在yield返回后的下一行恢复执行。
Erik Lippert 有一个关于迭代器其他方面的精彩系列
| 归档时间: |
|
| 查看次数: |
816 次 |
| 最近记录: |