Basically it was an oversight. In C# 1.0, foreach
never called Dispose
. With C# 1.2 (introduced in VS2003 - there's no 1.1, bizarrely) foreach
began to check in the finally
block whether or not the iterator implemented IDisposable
- they had to do it that way, because retrospectively making IEnumerator
extend IDisposable
would have broken everyone's implementation of IEnumerator
. If they'd worked out that it's useful for foreach
to dispose of iterators in the first place, I'm sure IEnumerator
would have extended IDisposable
.
When C# 2.0 and .NET 2.0 came out, however, they had a fresh opportunity - new interface, new inheritance. It makes much more sense to have the interface extend IDisposable
so that you don't need an execution-time check in the finally block, and now the compiler knows that if the iterator is an IEnumerator<T>
it can emit an unconditional call to Dispose
.
EDIT: It's incredibly useful for Dispose
to be called at the end of iteration (however it ends). It means the iterator can hold on to resources - which makes it feasible for it to, say, read a file line by line. Iterator blocks generator Dispose
implementations which make sure that any finally
blocks relevant to the "current point of execution" of the iterator are executed when it's disposed - so you can write normal code within the iterator and clean-up should happen appropriately.