Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I use covariance with two generic type parameters?

Consider the following example:

class Base {}

class Derived : Base {}

class Test1
{
    private List<Derived> m_X;

    public IEnumerable<Base> GetEnumerable()
    {
        return m_X;
    }
}

This compiles just fine, because IEnumerable<T> is covariant in T.

However, if I do exactly the same thing but now with generics:

class Test2<TBase, TDerived> where TDerived : TBase
{
    private List<TDerived> m_X;

    public IEnumerable<TBase> GetEnumerable()
    {
        return m_X;
    }
}

I get the compiler error

Cannot convert expression type 'System.Collection.Generic.List' to return type 'System.Collection.Generic.IEnumerable'

What am I doing wrong here?

like image 867
Vincent van der Weele Avatar asked Feb 11 '15 14:02

Vincent van der Weele


People also ask

What is covariance and Contravariance in generic?

Covariance and contravariance are terms that refer to the ability to use a more derived type (more specific) or a less derived type (less specific) than originally specified. Generic type parameters support covariance and contravariance to provide greater flexibility in assigning and using generic types.

What is the difference between covariance and Contravariance?

In C#, covariance and contravariance enable implicit reference conversion for array types, delegate types, and generic type arguments. Covariance preserves assignment compatibility and contravariance reverses it.

Can you be declared as contravariant?

A type can be declared contravariant in a generic interface or delegate only if it defines the type of a method's parameters and not of a method's return type. In , ref , and out parameters must be invariant, meaning they are neither covariant nor contravariant.

What is out T in C#?

" out T " means that type T is "covariant". That restricts T to appear only as a returned (outbound) value in methods of the generic class, interface or method. The implication is that you can cast the type/interface/method to an equivalent with a super-type of T .


1 Answers

Thing is, in the first case, the Base is known to be a class. In the second case, the type parameter T could be either class or a struct (this is how compiler thinks).

Solve the case by specifying that T is a class, and the error will disappear:

class Test2<TBase, TDerived> where TDerived : class, TBase
{
    private List<TDerived> m_X;

    public IEnumerable<TBase> GetEnumerable()
    {
        return m_X;
    }
}

So, the compiler tries to show us that TDerived could be a struct (since you didn't specify class constraint) and as we already know, covariance and contravariance do not work with structs.

like image 110
AgentFire Avatar answered Sep 18 '22 18:09

AgentFire