对于迭代器方法中的每个循环

for each loop in an iterator method

查看此程序:

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.

迭代器块是惰性的。您需要通过在 foreach 或其他内容中调用它来调用它。迭代器块中的代码只会在第一次调用 MoveNext 时执行,而 foreach 会为你执行。

截至目前,您只是在准备查询,您需要通过调用 GetEnumerator 然后调用 MoveNext.

来实现它

例如,以下代码将按预期失败。

static void Main()
{
    foreach(var item in GetLinks())
        Console.WriteLine(item );
    Console.WriteLine("Program failed!");
}

进一步阅读Iterator block implementation details

这是因为惰性执行。尝试使用函数的结果,例如:

GetLinks().First();

迭代器块是一种有点特殊的方法...当它看到 yield return 关键字时,编译器会生成一个 class 实现 IEnumerator<T>(以及 IEnumerable<T>,具体取决于您的方法的 return 类型),并将您的方法更改为 return 此 class 的一个实例。您的方法的原始主体被转换为枚举器的 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;
}