收益率的工作模式
当我有一个代码块
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; } }
我可以将收益率回归的原理解释为
- Main()调用SupplyIntegers()
-
|1| |2| |3| are stored in contiguous memory block.Pointer of "IEnumerator" Moves to |1|
- 控制从SupplyInteger()返回到Main()。
- Main()打印该值
- 指针移动到| 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()
,并跟踪您当前在迭代过程中的位置…在上面的示例中,它还将跟踪要枚举的列表中的值。