Click here to monitor SSC

Simon Cooper

Why enumerator structs are a really bad idea (redux)

Published Thursday, May 20, 2010 11:13 AM

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):

sealed class EnumeratorWrapper : IEnumerator<int> {

    private LinkedList<int>.Enumerator m_Enumerator;

    public EnumeratorWrapper(LinkedList<int> linkedList) {
        m_Enumerator = linkedList.GetEnumerator();
    }

    public int Current {
        get { return m_Enumerator.Current; }
    }

    object System.Collections.IEnumerator.Current {
        get { return Current; }
    }

    public bool MoveNext() {
        return m_Enumerator.MoveNext();
    }

    public void Reset() {
        ((System.Collections.IEnumerator)m_Enumerator).Reset();
    }

    public void Dispose() {
        m_Enumerator.Dispose();
    }
}

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:

.method public hidebysig newslot virtual final instance void Reset() cil managed
{
    .maxstack 8
    L_0000: nop 
    L_0001: ldarg.0 
    L_0002: ldfld valuetype 
        [System]System.Collections.Generic.LinkedList`1/Enumerator<int32> 
            EnumeratorWrapper::m_Enumerator
    L_0007: box 
        [System]System.Collections.Generic.LinkedList`1/Enumerator<int32>
    L_000c: callvirt instance void [mscorlib]System.Collections.IEnumerator::Reset()
    L_0011: nop 
    L_0012: ret 
}

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.

Comments

 

Jason Haley said:

Interesting Finds: May 20, 2010
May 20, 2010 6:11 AM
 

Interesting Finds: May 24, 2010 « Hank Wallace said:

May 24, 2010 1:58 PM
You need to sign in to comment on this blog

About Simon Cooper

Simon joined Red Gate as a software developer in 2007. He started off in the SQL Tools division, working on SQL Compare 7 and 8, moved onto Schema Compare for Oracle, and is now in the .NET division working on SmartAssembly.
<May 2010>
SuMoTuWeThFrSa
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345
Exploring SSIS Architecture and Execution History Through Scripting
 When you are using SSIS, there soon comes a time when you are confronted with having to do a tricky... Read more...

A Testing Perspective of Controllers and Orchestrators
 The neat separation between processing and rendering in ASP.NET MVC guarantees you an application... Read more...

TortoiseSVN and Subversion Cookbook Part 4: Sharing Common Code
 Michael Sorens continues his series on Source Control with Subversion and TortoiseSVN by describing... Read more...

How to Kill a Company in One Step or Save it in Three
 The majority of companies that suffer a major data loss subsequently go out of business. Wesley David... Read more...

Migrating from OCS 2007 R2 to Lync: Part 4
 Having migrated the rest of our users and legacy resources across and started getting ready to... Read more...