tags:

views:

491

answers:

2

I'm having problems implementing IEnumerable<T> in my custom collection class in C++/CLI. Here is the relevant part of the code:

using namespace System::Collections::Generic;

ref class MyCollection : IEnumerable<MyClass^>
{
public:
    MyCollection()
    {
    }  

    virtual IEnumerator<MyClass^>^ GetEnumerator()
    {
        return nullptr;
    }
};

When compiled, this results in the following errors:

error C2392: 'System::Collections::Generic::IEnumerator ^MyCollection::GetEnumerator(void)': covariant returns types are not supported in managed types, otherwise 'System::Collections::IEnumerator ^System::Collections::IEnumerable::GetEnumerator(void)' would be overridden error C3766: 'MyCollection' must provide an implementation for the interface method 'System::Collections::IEnumerator ^System::Collections::IEnumerable::GetEnumerator(void)'

This makes sense, since IEnumerable<T> derives from IEnumerable. However, I'm not sure how to fix this compile error. If this was C#, I would implicitly implement IEnumerable, however I'm not sure how to do that in C++/CLI (if that's even possible) like this:

class MyCollection : IEnumerable<MyClass>
{
    public MyCollection()
    {
    }

    public IEnumerator<MyClass> GetEnumerator()
    {
        return null;
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

If I do add an implementation of IEnumerable::GetEnumerator(), the compiler complains about two methods that differ only by return type (which also makes sense).

So, how do I implement IEnumerable<T> in a C++/CLI class?

+2  A: 

You must provide an explicit implementation of the non-generic GetEnumerator() method and include the non-generic namespace:

using namespace System::Collections;

....

virtual IEnumerator^ EnumerableGetEnumerator() = IEnumerable::GetEnumerator()
{
    return GetEnumerator<MyClass^>();
}

Update: As mentioned in the comments, the explicit version of GetEnumerator must be named different to avoid name clash, thus I've named it EnumerableGetEnumerator.

Similarly, in C# you would have to do it like this:

using namespace System.Collections.Generic;

public class MyCollection : IEnumerable<MyClass>
{
    public MyCollection()
    {
    }  

    public IEnumerator<MyClass> GetEnumerator()
    {
        return nullptr;
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator<MyClass>();
    }
}
Peter Lillevold
Just implementing GetEnumerator() doesn't work. If I do, I get a compiler error stating that overloaded functions can't differ by only return type.
Andy
Updated my question with how I would implement it in C#
Andy
Guess I was a bit quick here... NOW it should work. If not I will shoot myself in the foot.
Peter Lillevold
Yeah, that works. I had just found the same thing from Google. Thanks for the help. :)
Andy
One slight change: GetEnumerator() in your first code sample needs to be renamed to something else to avoid the name clash. I used GetEnumerator2(), although it doesn't really matter.
Andy
Glad to be of assistance (and to brush up on my C++/CLI)!
Peter Lillevold
+2  A: 

It is not very straightforward. Here is my stab at it. Fill in the "blanks". One of the biggest issues is the ambiguity if you use both Collections and Collections::Generic namespace. C++/CLI is really a pain.

using namespace System;
using namespace System::Collections;

public ref struct Enumerable : public Generic::IEnumerable<String^> {

public:
    virtual Generic::IEnumerator<String^>^ GetEnumerator() sealed = Generic::IEnumerable<String^>::GetEnumerator { 
        return gcnew Enumerator(); 
    }

    virtual IEnumerator^ GetEnumeratorBase() sealed = IEnumerable::GetEnumerator { 
        return GetEnumerator(); 
    }

private:
    ref struct TagEnumerator : public Generic::IEnumerator<String^> {

    public:
        property String^ Current { 
            virtual String^ get() {
                throw gcnew NotImplementedException(); 
            } 
        };

        property Object^ CurrentBase { 
            virtual Object^ get() sealed = IEnumerator::Current::get { 
                throw gcnew NotImplementedException(); 
            } 
        };

        virtual bool MoveNext() { 
            throw gcnew NotImplementedException(); 
        }

        virtual void Reset() { 
            throw gcnew NotImplementedException(); 
        }

        virtual ~Enumerator() {
        }
    };
};
GotVino