有人可以解释这个懒惰的评估代码吗?

所以,这个问题刚刚被问到:

如何处理“无限”IEnumerable?

我的示例代码:

public static void Main(string[] args) { foreach (var item in Numbers().Take(10)) Console.WriteLine(item); Console.ReadKey(); } public static IEnumerable Numbers() { int x = 0; while (true) yield return x++; } 

有人可以解释为什么这是懒惰的评估? 我在Reflector中查找了这段代码,而且比起初时我更困惑。

reflection器输出:

 public static IEnumerable Numbers() { return new d__0(-2); } 

对于numbers方法,看起来为该表达式生成了一个新类型:

 [DebuggerHidden] public d__0(int 1__state) { this.1__state = 1__state; this.l__initialThreadId = Thread.CurrentThread.ManagedThreadId; } 

这对我没有意义。 我会认为这是一个无限循环,直到我把代码放在一起并自己执行。

编辑 :所以我现在明白了.Take()可以告诉foreach枚举已经’结束’,当它真的没有时,但是不应该在它被完全调用之前调用它(而不是链接到Take() ? Take结果是实际被列举的内容,对吗? 但是,如果没有对Numbers进行全面评估,那么如何执行?

EDIT2 :这只是’yield’关键字强制执行的特定编译器技巧吗?

这不是一个无限循环的原因是你只根据Linq的Take(10)调用使用10次枚举。 现在,如果您编写的代码如下:

 foreach (var item in Numbers()) { } 

现在这是一个无限循环,因为您的枚举器将始终返回一个新值。 C#编译器接受此代码并将其转换为状态机。 如果你的枚举器没有一个保护条款来打破执行,那么调用者必须在你的样本中做哪个。

代码惰性的原因也是代码工作的原因。 基本上Take返回第一个项目,然后你的应用程序消耗,然后它需要另一个,直到它已经采取了10个项目。

编辑

这实际上与添加take无关。 这些被称为迭代器。 C#编译器对您的代码执行复杂的转换,从您的方法中创建枚举器。 我建议阅读但基本上(这可能不是100%准确),您的代码将进入Numbers方法,您可以设想启动状态机。

一旦你的代码达到收益率回报,你本质上是说Numbers()停止执行给他们回到这个结果,然后当他们要求下一个项目恢复执行时,在收益率返回后的下一行。

Erik Lippert在迭代器的misc方面有很多系列

这必须与:

  • 调用某些方法时iEnumerable的作用
  • 枚举的性质和Yield语句

当你枚举任何类型的IEnumerable时,该类会为你提供它将给你的下一个项目。 它没有对它的所有项目做任何事情,它只是给你下一个项目。 它决定该项目将是什么。 (例如,有些集合是有序的,有些则不是。有些不保证特定的订单,但似乎总是按照你放入它们的顺序给它们。)。

IEnumerable扩展方法Take()将枚举10次,获得前10个项目。 你可以做Take(100000000) ,它会给你很多数字。 但你只是在做Take(10) 。 它只是询问Numbers()下一个项目。 。 。 10倍。

这10个项目中的每一个, Numbers给出了下一个项目。 要了解如何操作,您需要阅读Yield语句。 对于更复杂的东西来说,它是语法糖 。 产量非常强大。 (我是一名VB开发人员,非常恼火,我仍然没有。)这不是一个function; 它是具有某些限制的关键字。 它使定义枚举器比其他方式更容易。

其他IEnumerable扩展方法总是遍历每个项目。 调用.AsList会炸掉它。 使用它大多数LINQ查询都会将其搞砸。

基本上,您的Numbers()函数会创建一个Enumerator。
foreach将在每次迭代中检查枚举器是否已到达结束,如果没有,则将继续。 你的特定囚犯将永远不会结束,但这并不重要。 这是懒惰评估。
枚举器将生成​​“实时”结果。
这意味着如果你要写。在那里拿(3),循环只会被执行三次。 枚举器仍然会在其中“留下”一些项目,但是由于目前没有方法需要它们,因此不会生成它们。
如果你试图像函数所暗示的那样生成从0到无穷大的所有数字,并且一次全部返回它们,那么只使用其中10个的程序会慢得多。 这是懒惰评估的好处 – 从未使用过的东西从未计算过。