Collections normally don't implement enumerators directly. Instead, they provide enumerators via IEnumerable interface:
The benefit of implementing this interface in a collection is that several consumers of the collection can enumerate it without interfering with each other.
Enumerable can be thought of as "IEnumeratorProvider".
So we could walk a collection like this:
However we have a great syntactic sugar for Enumerable: foreach.
The compiler will convert the former foreach into the previous enumeration using the underlying Enumerator.
We normally will make use of the generic counterparts, IEnumerator<T> and IEnumerable<T>.
One of the main advantages of using the generic version is that IEnumerator<T> inherits from IDisposable. This allows us to have a collection on which every element can be disposed, like a database connection.
So let's go to the point of this post. Let's see real magic in action.
1. This is what we write:
2. Compiler converts this to use an using block because of IDisposable nature of IEnumerable + Compiler converts it to use underlying enumerator:
3. Compiler converts this using block to call Dispose defined in IDisposable:
(I know I have a compiling error but you get the idea right? :))
If we compare the first piece with the last one we can save a few lines of code.
This is the magic that happens behind the scenes if we call a foreach using a disposable collection.