当我有代码块时
static void Main()
{
foreach (int i in YieldDemo.SupplyIntegers())
{
Console.WriteLine("{0} is consumed by foreach iteration", i);
}
}
class YieldDemo
{
public static IEnumerable<int> SupplyIntegers()
{
yield return 1;
yield return 2;
yield return 3;
}
}
我可以将收益率回报的原理解释为
|1| |2| |3| are stored in contiguous memory block.Pointer of "IEnumerator" Moves to |1|
澄清:
(1)通常我们将在函数内部允许一个有效的return语句。如何 C#处理多个yield return,yield return,...语句是否存在?
(2)一旦遇到回报,再也无法再进行控制 SupplyIntegers(),如果允许则不会再从1开始收益率?我的意思是收益率1?
答案 0 :(得分:33)
不 - 远离它;我会为你写一个长手版......它太蹩脚了!
请注意,如果您了解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<int> SupplyIntegers()
{
return new YieldEnumerable();
}
class YieldEnumerable : IEnumerable<int>
{
public IEnumerator<int> GetEnumerator()
{
return new YieldIterator();
}
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
class YieldIterator : IEnumerator<int>
{
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;
(或类似)C#编译器执行此操作的方式非常复杂,但它使编写迭代器变得轻而易举。
答案 1 :(得分:3)
它只是一个语法糖,.net为你生成IEnumerator类并实现MoveNext,Current和Reset方法,而不是生成IEnumarable类GetEnumerator,它返回IEnumerator,你可以通过.net reflector或ildasm看到魔术类。 / p>
另见here
答案 2 :(得分:2)
简单地说,迭代器块(或具有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
的方法调用。
答案 3 :(得分:1)
简而言之,(当你等待marc的长版本时)当编译器看到yield语句时,它会在幕后为你构建一个新的自定义类实例,它实现了一个名为IEnumerator
的接口,有方法Current()
和MoveNext()
,并跟踪您当前在迭代过程中的位置...在上面的示例中,它还会跟踪列表中的值通过列举。