The code of an IEnumerator
containing yield
is converted in two parts.
A new IEnumerator
class is created, which contains your original code, albeit rewritten so that it executes as you'd expect. This is where your original code, include the Console.WriteLine
lives.
The GetEnumerator()
method contains brand-new generated code that simply instantiates the IEnumerator
defined above and returns it.
As a consequence, none of your code runs before the first MoveNext()
is called.
This is why you'd often see the following pattern, e.g. to perform arguments validation immediately. The implementation is split in two parts: a normal method and a method containing yield
.
public IEnumerator<int> GetEnumerator(string whatever)
{
// Perform validation immediately when called
if (whatever == null) throw new ArgumentException();
return GetEnumeratorInternal(whatever);
}
private IEnumerator<int> GetEnumeratorInternal(string whatever)
{
// Everything in this method happens on first MoveNext
yield return 1;
yield return 2;
}