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?
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.
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.
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.
" 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 .
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With