Why enumerator structs are a really bad idea (redux)

Comments 0

Share to social media

My previous blog post went into some detail as to why calling MoveNext on a BCL generic collection enumerator didn’t quite do what you thought it would. This post covers the Reset method.

To recap, here’s the simple wrapper around a linked list enumerator struct from my previous post (minus the readonly on the enumerator variable):

If you have a look at the Reset method, you’ll notice I’m having to cast to IEnumerator to be able to call Reset on m_Enumerator. This is because the implementation of LinkedList<int>.Enumerator.Reset, and indeed of all the other Reset methods on the BCL generic collection enumerators, is an explicit interface implementation.

However, IEnumerator is a reference type. LinkedList<int>.Enumerator is a value type. That means, in order to call the reset method at all, the enumerator has to be boxed. And the IL confirms this:

On line 0007, we’re doing a box operation, which copies the enumerator to a reference object on the heap, then on line 000c calling Reset on this boxed object. So m_Enumerator in the wrapper class is not modified by the call the Reset. And this is the only way to call the Reset method on this variable (without using reflection). It is impossible to reset a BCL enumerator used as an unboxed value type.

Therefore, the only way that the collection enumerator struct can be used safely is to store them as a boxed IEnumerator<T>, and not use them as value types at all.

Load comments

About the author

Simon Cooper's contributions