由于interators的工作方式,这条线yield break;
永远不会运行。
GetTimes(int count)
执行时不执行迭代器方法
var times = GetTimes(2);
相反,它会在您从中提取值时执行(例如,当您执行 时times.Take(1).Single().ToString()
)。
这里有两件事会产生这种看似奇怪的行为:
yield return
每当命中一行时,迭代器就会停止。当您尝试从中获取另一个元素时,迭代器执行从它离开的点恢复。如果你不这样做,它将永远不会恢复执行。
您实际上正在执行迭代器两次。你做了一些通常被称为“IEnumerable 的多重枚举”的事情
为了说明幕后实际发生的情况,让我们对您的GetTimes
方法进行一个小改动:我们不要每次都返回相同的日期,但每次调用它时,我们都会返回前一个日期 + 1 天。让我们也添加一些Console.WriteLine
来跟踪执行。因此,新方法体可能如下所示:
IEnumerable<DateTime> GetTimes(int count)
{
for (int i = 0; i < count; i++)
{
Console.WriteLine("returning the value with index " + i);
yield return DateTime.Now.AddDays(i);
}
Console.WriteLine("About to hit the `yield break`! Awesome!");
yield break;
}
现在运行您的代码会产生以下输出:
返回索引为 0 的值
第一个元素:2012 年 11 月 16 日晚上 11:34:46
返回索引为 0 的值
返回索引为 1 的值
第二个元素:2012 年 11 月 17 日晚上 11:34:46
完成的...
这说明了上面的两点:
GetTimes
一旦返回一个值,执行就会停止,一旦请求另一个值,它就会从相同的状态恢复。
迭代器执行两次(第二次使用请求一个值,times
请求下一个值并使用它)。Skip
Take
好的,但是为什么不yield break;
执行呢?那是因为您的迭代器可以产生 2 个值并且它只被调用 2 次,使其在第二个yield return
被击中后冻结。如果您要从迭代器请求第三个元素,那么您的break
行将被命中。
现在,为了说明最后一行被命中的场景,让我们以通常的方式使用枚举器(使用foreach
循环)。将您的Console.WriteLine
行替换为:
foreach (var dateTime in times)
Console.WriteLine(dateTime);
此代码将产生以下输出:
返回索引为 0 的值
2012 年 11 月 17 日上午 12:05:20
返回索引为 1 的值
2012 年 11 月 18 日上午 12:05:20
即将达到“收益突破”!惊人的!
正如您所看到的,foreach
一直消耗迭代器到最后并且该yield break
行被命中。您也可以通过在其上设置断点来确认这一点。