【发布时间】:2017-12-04 11:10:52
【问题描述】:
我试图理解为什么下面的代码会这样:
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication
{
internal class Program
{
private static IEnumerable<int> Foo()
{
Console.WriteLine("Hello world!");
for (var i = 0; i < 10; i++)
yield return i;
}
public static void Main(string[] args)
{
var x = Foo();
var y = x.Take(3);
foreach (var i in y)
Console.WriteLine(i);
var z = x.Skip(3);
foreach (var i in z)
Console.WriteLine(i);
}
}
}
在 main 中,我得到了一个名为 x 的新“生成器”(请原谅 python 术语),然后我尝试枚举它两次。 我第一次获取前 3 个元素并打印它们:我希望程序打印“Hello world!”后面是从 0 到 2 的数字。 第二次我采用相同的可枚举并跳过另外 3 个元素。 我希望程序打印从 6 到 9 的数字,而不打印“Hello world!”首先。
相反,程序第二次打印“Hello world!”第二次,然后从 3 开始枚举,直到 9。
我不明白:为什么 Foo() 被调用了两次?
EDIT1:不重复。链接的“重复”询问编写枚举时的最佳实践,在这里我无法使用它的值。
EDIT2:我接受了一个老实说不是很好的答案,但包含一个指向让我理解问题的参考的链接。 让我感到困惑的是 foreach 循环在循环 IEnumerable 时实际上是如何工作的,所以我将在这里解释它以供将来参考。
IEnumerable 是值的序列 - 底层实现无关紧要,只要它可以按顺序为您提供值。
当您在其上使用 foreach 循环时,它会创建一个 Enumerator 对象,该对象基本上是一个游标,用于跟踪循环到达的位置。
所以发生的事情是第一个循环创建了第一个枚举器,它又必须启动我的生成器方法并打印第一个字符串。
第二个循环无法拾取前一个枚举器(正如我所相信的那样)。 相反,它实例化了第二个 Enumerator,然后重新启动生成器,从而打印第二个字符串。
对于阅读本文的任何 pythonista,请注意这与生成器在 python 中的工作方式截然相反(重用可迭代对象不会重新启动它)。
【问题讨论】:
-
我不同意它是重复的。该问题询问编写可迭代对象时的最佳实践;我试图了解为什么我的发电机会被重置,即使我不希望它会这样做。
-
x 不会在后续调用之间保存状态,您始终会获得由 Foo() 生成的完整枚举(包括副作用)。
-
@HelmutD,谢谢,这最终是原因。我来自 python,它是生成器本身维护其状态。
标签: c# ienumerable