收益率的工作模式

当我有一个代码块

static void Main() { foreach (int i in YieldDemo.SupplyIntegers()) { Console.WriteLine("{0} is consumed by foreach iteration", i); } } class YieldDemo { public static IEnumerable SupplyIntegers() { yield return 1; yield return 2; yield return 3; } } 

我可以将收益率回归的原理解释为

  1. Main()调用SupplyIntegers()
  2. |1| |2| |3| are stored in contiguous memory block.Pointer of "IEnumerator" Moves to |1|
  3. 控制从SupplyInteger()返回到Main()。
  4. Main()打印该值
  5. 指针移动到| 2 |,依此类推。

澄清:

(1)通常我们将在函数内部允许一个有效的return语句。当多个yield return,yield return,…语句出现时C#如何处理?

(2)一旦遇到回报,就无法再次控制回到SupplyIntegers(),如果允许则不会再次从1开始收益? 我的意思是收益率1?

不 – 远离它; 我会为你写一个长手版……它太蹩脚了!


请注意,如果您了解foreach实际上是有帮助的:

 using(var iterator = YieldDemo.SupplyIntegers().GetEnumerator()) { int i; while(iterator.MoveNext()) { i = iterator.Current; Console.WriteLine("{0} is consumed by foreach iteration", i); } } 

 using System; using System.Collections; using System.Collections.Generic; static class Program { static void Main() { foreach (int i in YieldDemo.SupplyIntegers()) { Console.WriteLine("{0} is consumed by foreach iteration", i); } } } class YieldDemo { public static IEnumerable SupplyIntegers() { return new YieldEnumerable(); } class YieldEnumerable : IEnumerable { public IEnumerator GetEnumerator() { return new YieldIterator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } class YieldIterator : IEnumerator { private int state = 0; private int value; public int Current { get { return value; } } object IEnumerator.Current { get { return Current; } } void IEnumerator.Reset() { throw new NotSupportedException(); } void IDisposable.Dispose() { } public bool MoveNext() { switch (state) { case 0: value = 1; state = 1; return true; case 1: value = 2; state = 2; return true; case 2: value = 3; state = 3; return true; default: return false; } } } } 

如您所见,它在迭代器中构建状态机,状态机由MoveNext 。 我已经将模式与state字段一起使用,因为您可以看到这对于更复杂的迭代器是如何工作的。

重要的:

  • 迭代器块中的任何变量都将成为状态机上的字段
  • 如果你有一个finally块(包括using ),它会进入Dispose()
  • 导致yield return部分代码成为一个case (粗略地)
  • yield break变为state = -1; return false; state = -1; return false; (或类似的)

C#编译器执行此操作的方式非常复杂,但它使编写迭代器变得轻而易举。

它只是一个语法糖,.net为你生成IEnumerator类并实现MoveNext,Current和Reset方法,而不是生成IEnumarable类GetEnumerator,它返回IEnumerator,你可以通过.net reflector或ildasm看到魔术类。

另见这里

简单地说,迭代器块(或带有yield语句的方法,如果可能)由编译器转换为编译器生成的类。 此类实现IEnumerator ,并将yield语句转换为该类的“状态”。

例如,这个:

 yield return 1; yield return 2; yield return 3; 

可能会变成类似于:

 switch (state) { case 0: goto LABEL_A; case 1: goto LABEL_B; case 2: goto LABEL_C; } LABEL_A: return 1; LABEL_B: return 2; LABEL_C: return 3; 

迭代器块可以看作是抽象状态机。 这段代码将由IEnumerator的方法调用。

简而言之,(当等待marc的长版本时)当编译器看到yield语句时,它会在幕后为你构建一个新的自定义类实例,它实现了一个名为IEnumerator的接口,它有方法Current() ,以及MoveNext() ,并跟踪您当前在迭代过程中的位置…在上面的示例中,它还将跟踪要枚举的列表中的值。