I noticed that the generic
IEnumerator<T> inherits from IDisposable, but the non-generic interface IEnumerator does not. Why is it designed in this way?
Usually, we use foreach statement to go through a
IEnumerator<T> instance. The generated code of foreach actually has try-finally block that invokes Dispose() in finally.
Basically it was an oversight. In C# 1.0,
foreach never called
Dispose 1. 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
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
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
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.
1 Looking back at the 1.0 spec, it was already specified. I haven’t yet been able to verify this earlier statement that the 1.0 implementation didn’t call
IEnumerable<T> doesn’t inherit IDisposable. IEnumerator<T> does inherit IDisposable however, whereas the non-generic IEnumerator doesn’t. Even when you use foreach for a non-generic IEnumerable (which returns IEnumerator), the compiler will still generate a check for IDisposable and call Dispose() if the enumerator implements the interface.
I guess the generic Enumerator<T> inherits from IDisposable so there doesn’t need to be a runtime type-check—it can just go ahead and call Dispose() which should have better performance since it can be probably be optimized away if the enumerator has an empty Dispose() method.
I know this is an old discussion but I reasontly wrote a library where I used IEnumerable of T/IEnumerator of T where users of the library could implement custom iterators they should just implement IEnumerator of T.
I found it very strange that IEnumerator of T would inherit from IDisposable. We implement IDisposable if we want to free unmanged resources right? So it would only be relevant for enumerators that actually hold unmanaged resources – like an IO stream etc. Why not just let users implement both IEnumerator of T and IDisposable on their enumerator if it makes sense? In my book this violates the single responsibility principle – Why mix enumerator logic and disposing objects.
Does IEnumerable` inherit IDisposing? According to the .NET reflector or MSDN. Are you sure you’re not confusing it with IEnumerator? That uses IDisposing because it only for enumerating a collection and not meant for longevity.
A bit hard to be definitive on this, unless you manage to get a response from AndersH himself, or someone close to him.
However, my guess is that it relates to the “yield” keyword that was introduced in C# at the same time. If you look at the code generated by the compiler when “yield return x” is used, you’ll see the method wrapped up in a helper class that implements IEnumerator; having IEnumerator descend from IDisposable ensures that it can clean up when enumeration is complete.
IIRC The whole thing about having
IEnumerable is a result of
IEnumerable predating .Net’s template stuff. I suspect that your question is in the same way.