Here is the same countdown, written the way you will write it from now on.
Forty lines become five
By hand
class Countdown : IEnumerable<int>
{
private readonly int _from;
public Countdown(int from)
=> _from = from;
public IEnumerator<int> GetEnumerator()
=> new CountdownEnumerator(_from);
IEnumerator IEnumerable.GetEnumerator()
=> GetEnumerator();
// ... plus a nested class with
// a field, MoveNext, Current,
// Reset and Dispose
}With yield return
static IEnumerable<int> Countdown(int from)
{
for (int i = from; i >= 1; i--)
{
yield return i;
}
}Same sequence, same state machine at runtime. The compiler generates the class on the right-hand side too — you just stopped writing it.
What makes a method an iterator
A method is an iterator if its body contains yield return or yield break. That is the entire
rule. There is no keyword on the signature and no attribute — the compiler notices the yield and
rewrites the whole method.
Two things follow, and both surprise people:
- The return type must be
IEnumerable,IEnumerable<T>,IEnumeratororIEnumerator<T>. Nothing else is allowed. - The body you wrote never runs as written. It becomes a
MoveNextmethod that jumps back to wherever it last left off.
static IEnumerable<string> Greetings()
{
Console.WriteLine("-- starting --");
yield return "hello";
Console.WriteLine("-- between --");
yield return "world";
Console.WriteLine("-- done --");
}
// foreach (var g in Greetings()) Console.WriteLine(g);
//
// -- starting --
// hello
// -- between --
// world
// -- done --
Read that output again. Execution pauses at each yield return and resumes on the next
MoveNext. The line after a yield return does not run until someone asks for another value.
That is the whole idea. Everything else in this course is a consequence of it.
Try it: Task 1
Open the editor