开发者

How do I implement IEnumerable in my Dictionary wrapper class that implements IEnumerable<Foo>?

开发者 https://www.devze.com 2023-04-11 07:16 出处:网络
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

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 IEnumera开发者_如何学编程tor.

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.


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>


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

}


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();
}


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();
}


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() { /* ... */ }


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.


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();
    }
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号