Click here to Skip to main content
15,891,423 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I have a method that takes an IEnumerator<T> as a parameter, iterates through a few items, and returns a result. My underlying type is a List<T>. If I use the result of List<T>.GetEnumerator as my parameter, the iterator doesn't seem to update after each call to my method. If I use IEnumerable<T>.GetEnumerator, everything works fine. Here's some sample code that demonstrates the problem:

static void Main(string[] args)
{
    var myData = new List<char> { 'a', 'b', 'c', 'd' };

    // This returns type List<char>.Enumerator
    var listEnum = myData.GetEnumerator();

    Console.WriteLine("Using List<char>.GetEnumerator():");
    for(Int32 i = 0; i < myData.Count; i++)
    {
        WriteEnumerator(listEnum);
    }

    Console.WriteLine("Using IEnumerable<char>.GetEnumerator():");
    // This returns type IEnumerator<char>
    var iEnum = ((ICollection<char>)myData).GetEnumerator();
    for (Int32 i = 0; i < myData.Count; i++)
    {
        WriteEnumerator(iEnum);
    }

    Console.WriteLine("Press any key to close");
    Console.ReadKey();            
}

private static void WriteEnumerator(IEnumerator<char> listEnum)
{
    listEnum.MoveNext();
    Console.WriteLine(listEnum.Current);
}

The first loop outputs
a
a
a
a

The second loop outputs
a
b
c
d

Any ideas why these two have different behavior? I tried looking at the .NET source for List<t>, and both implementations of GetEnumerator return a List<T>.Enumerator, so I don't see why they would behave differently.


As for why I'm not using a foreach loop with the IEnumerator, a colleague and I have implemented some extension method on IEnumerator<byte> like GetNextInt16(), GetNextDouble(), GetNextUInt32() that help make our code cleaner.
Posted
Comments
Clifford Nelson 12-Apr-12 20:47pm    
All I can figure out is that it is a bug, which just has not been caught.
dybs 12-Apr-12 20:55pm    
I got an email notification of a solution you posted with a link to an article (haven't read it yet). Any idea why I don't see the solution here?

1 solution

I tried to debug your code and noticed that listEnum and iEnum have different types. Looking at MSDN's documentation, the type of return value for List<T>.Enumerator[^] is System.Collections.Generic.List<T>.Enumerator[^], which is a struct. So every time you call the method, you pass a new copy to it, and that only the copy gets updated. On the other hand, iEnum has a type of System.Collections.Generic.IEnumerator<T>[^], which is an interface.

[Edit]In reply to comment of OP:
dybs wrote:
both List.GetEnumerator and the explicit interface implementation were simply return new Enumerator(this);

I agree that both method points to that code. However, ICollection<T> has inherited its GetEnumerator method from IEnumerable<T>. If you take a look at the source code for that interface, you can see that there is a method of type IEnumerator<T>. So basically, what happens is that, for iEnum, you are boxing the struct(which is a value type) to a reference type, where as, for listEnum, the value is a struct, since you are calling List<T>.GetEnumerator() directly. Below is the interface declaration of IEnumerable<T>

C#
public interface IEnumerable<T> : IEnumerable
    {
        /// <summary>
        ///               Returns an enumerator that iterates through the collection.
        ///           </summary>
        /// <returns>
        ///               A <see cref="T:System.Collections.Generic.IEnumerator`1" /> that can be used to iterate through the collection.
        ///           </returns>
        /// <filterpriority>1</filterpriority>
        IEnumerator<T> GetEnumerator();
    }


You might also find this post[^] useful.
[/Edit]
 
Share this answer
 
v4
Comments
dybs 12-Apr-12 21:23pm    
I noticed the different types (although I missed that it's a struct, good catch), but when I looked at the .NET source, both List<t>.GetEnumerator</t> and the explicit interface implementation were simply <pre>return new Enumerator(this);</pre>So even though the return types are different, they are still the same underlying type, right? So why would they behave differently?
walterhevedeich 12-Apr-12 22:11pm    
I have updated my answer. Hope it answers your question.
dybs 12-Apr-12 22:47pm    
Great explanation, and thanks for the MSDN blog link. I actually wasn't aware that structs were boxed when casting to an interface, but that does makes sense now. Thanks!
VJ Reddy 13-Apr-12 8:38am    
Nice explanation. +5

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900