Here is a little puzzle for C# developers reading my blog. What is the error in the program below?

   using System.Collections.Generic;
   class Program
   {
       public static void Main()
       {
           int[] arr = new int[10];
           IEnumerator<int> e = arr.GetEnumerator();
       }
   }

If you don’t see it, don’t worry.  I was surprised by this C# behavior as well. Just come back in a couple days to see the solution. Or try to compile the program in Visual Studio. :-)

UPDATE: Finally, I am back with a solution.

To understand what’s going on, let’s take a look at a fixed version of the code:

using System.Collections.Generic;
class Program
{
    public static void Main()
    {
        int[] arr = new int[10];
        IEnumerator<int> e = ((IEnumerable<int>)arr).GetEnumerator();
    }
}

The problem of the original code sample is that the GetEnumerator() method on arrays returns a non-generic IEnumerator. That’s because .NET arrays had GetEnumerator() even before there were generics in .NET.

When generics got introduced in .NET 2.0, arrays got another GetEnumerator() method, one that returns a generic IEnumerator<>. In order to have two GetEnumerator() methods with different return types, one of them had to be a part of an explicit interface implementation.

As a result, arr.GetEnumerator() binds to a method that returns a non-generic IEnumerator, and IEnumerable<int>)arr).GetEnumerator() binds to a method that returns a generic IEnumerator<int>. Somewhat surprising, but understandable.

Disclaimer: this explanation is based on public information and my guesses, and should not be considered official, authoritative, or anything like that.

Tags:

9 Comments to “Puzzling over arrays and enumerators in C#”

  1. Peter says:

    You get this error:

    Cannot implicitly convert type ‘System.Collections.IEnumerator’ to ‘System.Collections.Generic.IEnumerator<T>’.

    To make the code compilable you need to create a cast:

    IEnumerator<T> e = (IEnumerator<T>) arr.GetEnumerator();

    or safe cast:

    IEnumerator<T> e = arr.GetEnumerator() as IEnumerator<T>

  2. Paul says:

    A simple cast:

    IEnumerator e = (IEnumerator)arr.GetEnumerator();
    or
    IEnumerator e = arr.GetEnumerator() as IEnumerator;

    won’t have the desired effect. The former will throw a cast exception and the latter will result in e being null.

    It should first be converted to Enumerable and then get the Enumerator:

    IEnumerator e = arr.AsEnumerable().GetEnumerator();

    And now I’m hoping that this is wrong and there’s an easier way, because that’s damned ugly when you think about it …

  3. Paul says:

    <grumble> cut-and-paste bites me again, sorry for the duplicate

    A simple cast:

    IEnumerator<int> e = (IEnumerator<int>)arr.GetEnumerator();
    or
    IEnumerator<int> e = arr.GetEnumerator() as IEnumerator<int>;

    won’t have the desired effect. The former will throw a cast exception and the latter will result in e being null.

    It should first be converted to Enumerable and then get the Enumerator:

    IEnumerator<int> e = arr.AsEnumerable<int>().GetEnumerator();

    And now I’m hoping that this is wrong and there’s an easier way, because that’s damned ugly when you think about it …

  4. @Paul: Yeah, the comment system is very frustrating. I need to find a good comment plugin.

    You make an interesting point. I did not try casting the non-genetic enumerator to a generic one, but I am actually mildly surprised that you will get a cast exception.

    Igor
    PS.: A follow-up post is coming (haven’t gotten around to it yet).

  5. Paul J says:

    Was the follow-up post ever published? I don’t see it, but I’d like to know what you had in mind, if it wasn’t what (the other) Paul said.

  6. To my mind, the simplest way to make the code compile would be to declare e as IEnumerable. Also, instead of System.Collections.Generic, the code should use System.Collections because IEnumerable resides there. So, the updated code would become:

    using System.Collections;
    class Program
    {
    public static void Main()
    {
    int[] arr = new int[10];
    IEnumerator e = arr.GetEnumerator();
    }
    }

    I think the described behavior is perfectly reasonable. arr.GetEnumerator()(s return type is actually SZArrayEnumerator. Although SZArrayEnumerator and IEnumerator<T> both implement IEnumerator, they are different types and cannot cast implicitly to each other.

  7. Paul J: Thanks for the reminder. I updated the article with the solution.

  8. Anonymous says:

    Probably little bit more elegant solution is

    IEnumerable arr = new int[10];
    IEnumerator e = arr.GetEnumerator();

  9. Anonymous says:

    trying again to bypass the comment obfuscater

    [[ = greater then
    ]] lower than

    IEnumerable[[int]] arr = new int[10];
    IEnumerator[[int]] e = arr.GetEnumerator();

Leave a Reply

You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>