This is easier than it sounds. The compiler can decompose the iterator function into separate pieces; fragments are divided by yield .
The final computer just has to keep track of which piece we are currently in, and the next time the iterator is called, it jumps directly to that piece. We also need to track all local variables (of course).
Then we need to consider several special cases, in particular, loops containing yield s. Fortunately, IL (but not C # itself) allows goto to jump into loops and resume them.
Note that there are some very complex cases of cross, for example. C # does not allow yield in finally blocks, because it would be very difficult (impossible?) To leave the function after yield , and then resume the function, perform a cleanup, throw any exception again and save the stack trace.
Eric Lipper has published a detailed description of the process. (read the articles he related to, too!)
source share