Question

Referring to this test code compiled for C# in Visual Studio 2010 express

public class Test
{
    class Base { }
    class Derived : Base { }

    void Test1(IEnumerable<Derived> derived)
    {
        IEnumerable<Base> b = derived; //This works fine using covariance on IEnumerable
    }

    void Test2<TDerived, TBase>(TDerived derived) where TDerived : TBase
    {
        TBase b = derived; //This works fine because TDerived is constrained to derive from TBase
    }

    void Test3<TDerived, TBase>(IEnumerable<TDerived> derived) where TDerived : TBase
    {
        IEnumerable<TBase> b = derived; //ERROR: paraphrased: Cannot implicitly convert type IEnumerable<TDerived> to IEnumerable<TBase>
    }
}

I am attempting to utilize the covariance of IEnumerable to store an enumerable of a generic type parameter within an enumerable of a class that that type parameter is constrained to inherit from. This is exemplified by Test3. Note that Test1 and Test2 (demonstrating covariance on compile time types and assignment of constrained types respectively) both compile fine. It is the combination of the two language features which isn't working for me.

I am able to use IEnumerable<TBase> b = derived.Cast<TBase>() and be 100% confident that no cast will fail if my understanding is not flawed so I do have a workaround available. My question is, why does the compiler not allow this? Is this forbidden for some logical reason, an oversight in the compiler, or something else I haven't thought of?

Was it helpful?

Solution

Answer to initial question

You're currently trying to convert a single element of type TDerived to a sequence of type Base. I wouldn't expect your Cast call to work either, as TDerived doesn't implement IEnumerable - I suspect you've actually got that to work in a different situation instead.

I suspect you actually meant:

void Test3<TDerived>(IEnumerable<TDerived> derived) where TDerived : Base
{
    IEnumerable<Base> b = derived;
}

That compiles with no problems.

Answer to edited question

Okay, now we've got the real problem between two type parameters, the issue is that the compiler doesn't know that they're reference types - which is required for generic variance. You can fix that with a class constraint on TDerived:

void Test3<TDerived, TBase>(IEnumerable<TDerived> derived)
    where TDerived : class, TBase
{
    IEnumerable<TBase> b = derived;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top