11

I'm trying to create a wrapper for a Dictionary<String,Foo>.

Dictionary<String,Foo> implements IEnumerable<KeyValuePair<String,Foo>>, but I want my wrapper class to implement IEnumerable<Foo>. So I tried this:

public class FooCollection : IEnumerable<Foo>
{
    private Dictionary<string, Foo> fooDictionary = new Dictionary<string, Foo>();

    public IEnumerator<Foo> GetEnumerator()
    {
        return fooDictionary.Values.GetEnumerator();
    }

    // Other wrapper methods omitted

}

However I get this error:

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

However I don't understand this error, because FooCollection.GetEnumerator() returns an IEnumerator<Foo>, and IEnumerator<Foo> is an IEnumerator.

EDIT:

The solution of explicitly implementing IEnumerator.GetEnumerator() works. However I'm now wondering why when I "Go to definition" on a List<T> I see only one definition of GetEnumerator: public List<T>.Enumerator GetEnumerator();

Apparently List<T> can have a single GetEnumerator method that returns something that implements both IEnumerator<T> and IEnumerator, but I have to have one method for each?

EDIT:

As answered by LukeH below, List<T> does include the explicit interface implementations. Apparently Visual Studio just doesn't list those when generating method stubs from the metadata. (See this previous question: Why does the VS Metadata view does not display explicit interface implemented members )

Before posting this question I had tried checking List<T> (via "Go to Definition" in Visual Studio) to see if I needed to implement multiple versions of GetEnumerator. I guess this wasn't the most reliable way to check.

Anyway, I'm marking this as answered. Thanks for your help.

Community
  • 1
  • 1
Tim Goodman
  • 23,308
  • 7
  • 64
  • 83

7 Answers7

12

Add the following explicit interface implementation:

IEnumerator IEnumerable.GetEnumerator()
{
    return this.GetEnumerator();
}

Although IEnumerator<T> is an IEnumerator, the contract for IEnumerable returns an IEnumerator specifically, not an IEnumerator<T>

Matthew Abbott
  • 60,571
  • 9
  • 104
  • 129
  • LOL looks like he has his answer 4 ways. – Michael Brown Oct 07 '11 at 20:23
  • This works. But then how come List only appears to implement GetEnumerator one way? (Based on the metadata anyway, I just see `public List.Enumerator GetEnumerator();`) I figured if `List` only had to implement it once, the same would be true for me. – Tim Goodman Oct 07 '11 at 20:39
  • @TimGoodman `List` implements the `IEnumerable` and `IEnumerable` interfaces explicitly, and has one public method (which is the one that you see). – vcsjones Oct 07 '11 at 21:38
8

When implementing IEnumerable<T>, you must also explicitly implement IEnumerable.GetEnumerator(). The method for the generic interface is not valid in and of itself as an implementation for the non-generic method contract. You can have one call the other, or since you have a child object whose enumerator you are using, just copy/paste;

using System.Collections;
using System.Collections.Generic;

public class FooCollection : IEnumerable<Foo>
{
    private Dictionary<string, Foo> fooDictionary = new Dictionary<string, Foo>();

    public IEnumerator<Foo> GetEnumerator()
    {
        return fooDictionary.Values.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        //forces use of the non-generic implementation on the Values collection
        return ((IEnumerable)fooDictionary.Values).GetEnumerator();
    }

    // Other wrapper methods omitted

}
Juha Palomäki
  • 26,385
  • 2
  • 38
  • 43
KeithS
  • 70,210
  • 21
  • 112
  • 164
4

The problem is that there is no such thing as return type covariance in .NET - IEnumerator M() and IEnumerator<Foo> M() are completely different methods.

The workaround is that you have to implement the non-generic version explicitly:

System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
    // this calls the IEnumerator<Foo> GetEnumerator method
    // as explicit method implementations aren't used for method resolution in C#
    // polymorphism (IEnumerator<T> implements IEnumerator)
    // ensures this is type-safe
    return GetEnumerator();
}
thecoop
  • 45,220
  • 19
  • 132
  • 189
2

As long as generic IEnumerable{T} inherit IEnumerable You have to implement IEnumerable.GetEnumerator() as well. You can do it explicitly like:

IEnumerator IEnumerable.GetEnumerator()
{
       return GetEnumerator();
}
sll
  • 61,540
  • 22
  • 104
  • 156
2

You've already had several answers to your main question. I'll answer the question raised in your edit...

The List<T> class actually has three different GetEnumerator methods: The public method that's called when the compile-time instance is typed as List<T> itself, and two explicit interface implementations to meet the IEnumerable/IEnumerable<T> contracts. The enumerator objects returned by all three methods are all the same List<T>.Enumerator type behind-the-scenes.

// Public method
public List<T>.Enumerator GetEnumerator() { /* ... */ }

// IEnumerable<T> explicit interface implementation
IEnumerator<T> IEnumerable<T>.GetEnumerator() { /* ... */ }

// IEnumerable explicit interface implementation
IEnumerator IEnumerable.GetEnumerator() { /* ... */ }
LukeH
  • 263,068
  • 57
  • 365
  • 409
  • Thanks for the reply. When I hit "Go To Definition" on a List and then search (Ctrl-F) for GetEnumerator, I only see the public method. Perhaps Visual Studio just doesn't produce an accurate list from the metadata? – Tim Goodman Oct 07 '11 at 21:15
  • This is maybe a fairly minor quibble on my part, but I actually checked List to see if it implemented multiple GetEnumerator methods posting my original question, so I'm wondering how I was mislead. – Tim Goodman Oct 07 '11 at 21:17
  • Ah, according to this previous question, when Visual Studio generates method stubs from metadata, it doesn't include explicit interface implementations. http://stackoverflow.com/questions/7268632/why-does-the-vs-metadata-view-does-not-display-explicit-interface-implemented-mem – Tim Goodman Oct 07 '11 at 21:23
  • The other answers do not include the need for the for one of the enumerators - namely, IEnumerable.GetEnumerator() - that did the trick for me. Thanks – mdebeus Sep 25 '15 at 19:00
0

When you implement the generic IEnumerable interface, you also have to implement the non generic IEnumerable interface. The error is about the missing non generic method.

Michael Brown
  • 9,041
  • 1
  • 28
  • 37
0

Here's the declaration of IEnumerable:

public interface IEnumerable<out T> : IEnumerable
{
    new IEnumerator<T> GetEnumerator();
}

notice the new keyword.

Here's the declaration of IEnumerable:

public interface IEnumerable
{

    IEnumerator GetEnumerator();
}

So now you have a GetEnumerator method, but which one of those two are you implementing? Therefore, you need to add an explicit implementation of the non-generic version:

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
BFree
  • 102,548
  • 21
  • 159
  • 201