Search code examples
c#genericsinterfaceenumerator

Interface implementations and return types


The List<T> class implements the IEnumerable<T> interface. It has a method GetEnumerator that returns a List<T>.Enumerator.

I have a class as below, which gives a compile error saying the return type of GetEnumerator doesn't match the interface.

public class InsertionSortedSet<T> : IEnumerable<T>
{
    public struct Enumerator : IEnumerator<T>
    {
        // Required interface implemented
    }

    // Other interface methods implemented

    public Enumerator GetEnumerator()
    {
        return new Enumerator(this);
    }
}

'Entities.Helpers.InsertionSortedSet' does not implement interface member 'System.Collections.Generic.IEnumerable.GetEnumerator()'. 'Entities.Helpers.InsertionSortedSet.GetEnumerator()' cannot implement 'System.Collections.Generic.IEnumerable.GetEnumerator()' because it does not have the matching return type of 'System.Collections.Generic.IEnumerator'.

Given that List<T> seems to return it's own Enumerator class (not the interface) and yet it does implement the Enumeration<T> interface I'm confused, as I can't see what i've got different to that class.

What's wrong with my setup that makes it fail, where List<T> works?


I want to return a InsertionSortedSet<T>.Enumerator rather than the interface as it avoids boxing, which I need to cut out.


Solution

  • It's complaining because GetEnumerator() returns IEnumerator<T> for the IEnumerable<T> interface. To satisfy, your type must return IEnumerator<T> (and an explicit one for IEnumerator as well).

    But, many times it's desirable for a class to return a more specific type than the interface specifies, but interfaces don't allow for covariant return types like that. So to do this, you can do what List<T> does and have GetEnumerator() return your specific enumerator, but you must also implement explicit implementations for IEnumerable.GetEnumerator() that returns an IEnumerator and for IEnumerable<T>.GetEnumerator() that returns an IEnumerator<T>:

        // This is your specific version, like List<T> does
        public Enumerator GetEnumerator()
        {
            return new Enumerator(this);
        }
    
        // This is the one with the return value IEnumerator<T> expects
        IEnumerator<T> IEnumerable<T>.GetEnumerator()
        {
            return new Enumerator(this);
        }
    
        // Plus, it also expects this as well to satisfy IEnumerable
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    

    If you look at List<T> you'll see that it implements GetEnumerator() three times:

    • Once explicitly for IEnumerable<T>
    • Once explicitly for Enumerable
    • Once with a very list-specific return

    You could do the same in your class if you wish (which it sould like is the way you began) but if you do that you must explicitly implement IEnumerable<T>.GetEnumerator() and IEnumerable.GetEnumerator()

    If you navigate to the definition, you can see the other definitions in the object browser by selecting the different interfaces it satisfies (or by assigning a List<T> instance to an IEnumerable or IEnumerable<T> reference and going to definition):

    Screenshot of Object Explorer