【问题标题】:for each loop in an iterator method对于迭代器方法中的每个循环
【发布时间】:2015-01-23 10:38:18
【问题描述】:
看看这个程序:
static class Program
{
static void Main()
{
GetLinks();
Console.WriteLine("Program failed!");
}
static IEnumerable<string> GetLinks()
{
throw new Exception();
foreach (var item in new string[] { })
yield return item;
}
}
这很奇怪,但是这个程序的结果是Program failed!,这意味着GetLinks函数甚至没有被调用。
这种行为的解释是什么?
Check it out for yourself.
【问题讨论】:
标签:
c#
.net
foreach
iterator
unreachable-code
【解决方案1】:
迭代器块是惰性的。您需要通过在 foreach 或其他东西中调用它来调用它。迭代器块中的代码只会在第一次调用 MoveNext 时执行,foreach 将为您执行。
到目前为止,您只是在准备查询,您需要通过调用 GetEnumerator 后跟 MoveNext 来实现它。
例如,以下代码将按预期失败。
static void Main()
{
foreach(var item in GetLinks())
Console.WriteLine(item );
Console.WriteLine("Program failed!");
}
延伸阅读Iterator block implementation details
【讨论】:
-
这里是documentation: "...技术实现:对 MyIteratorMethod 的调用不执行方法的主体。而是调用返回一个 IEnumerable 到元素变量...".所以因为方法体甚至没有被执行,所以没有引发异常。
【解决方案2】:
迭代器块是一种有点特殊的方法...当它看到 yield return 关键字时,编译器会生成一个实现 IEnumerator<T> 的类(以及 IEnumerable<T>,具体取决于您方法的返回类型),并更改您的方法以返回此类的实例。您方法的原始主体转换为枚举器的MoveNext 方法。 这意味着你的方法体只有在枚举结果时才会执行。如果你不枚举结果,仅仅调用方法是没有效果的。换句话说,迭代器块是延迟执行的。
这就是为什么作为迭代器块实现的方法必须在单独的方法中验证它们的参数:
public IEnumerable<string> Foo(string arg)
{
if (arg == null) throw new ArgumentNullException();
return FooIterator(arg);
}
private IEnumerable<string> FooIterator(string arg)
{
yield return arg;
}
【解决方案3】:
这是因为延迟执行。尝试使用函数的结果,例如:
GetLinks().First();