When i have a code block
static void Main()
{
foreach (int i in YieldDemo.SupplyIntegers())
{
Console.WriteLine(\"{0} is consumed by foreach iteration\
Nope - far from it; I'll write a long-hand version for you... it is too grungy!
Note it also helps if you understand that the foreach
is actually:
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;
}
}
}
}
As you can see, it builds a state machine in the iterator, with the state machine progressed by MoveNext
. I've used the pattern with a state
field, as you can see how this would work for more complex iterators.
Importantly:
finally
block (including using
), it goes in the Dispose()
yield return
become a case
(roughly)yield break
becomes a state = -1; return false;
(or similar)The way the C# compiler does this is very complicated, but it makes writing iterators a breeze.