I have an interface with a covariant type parameter:
interface I<out T>
{
T Value { get; }
}
Additionally, I have a non-generic base class and another deriving from it:
class Base
{
}
class Derived : Base
{
}
Covariance says that an I<Derived>
can be assigned to an I<Base>
, and indeed I<Base> ib = default(I<Derived>);
compiles just fine.
However, this behavior apparently changes with generic parameters with an inheritance constraint:
class Foo<TDerived, TBase>
where TDerived : TBase
{
void Bar()
{
I<Base> ib = default(I<Derived>); // Compiles fine
I<TBase> itb = default(I<TDerived>); // Compiler error: Cannot implicitly convert type 'I<TDerived>' to 'I<TBase>'. An explicit conversion exists (are you missing a cast?)
}
}
Why are these two cases not treated the same?
Covariance says that an
I<Derived>
can be assigned to anI<Base>
Correct.
Why are these two cases not treated the same?
You are overgeneralizing from your statement. Your logic seems to go like this:
I<Derived>
can be assigned to an I<Base>
Derived
and Base
are arbitrary types that have a supertype-subtype relationship.Though plausible, that chain of logic is wrong. The correct chain of logic is:
I<Derived>
can be assigned to an I<Base>
Derived
and Base
are arbitrary reference types that have a superclass-subclass relationship.In your example one would be perfectly within rights to make a Foo<int, object>
. Since I<int>
cannot be converted to I<object>
, the compiler rejects a conversion from I<TDerived>
to I<TBase>
. Remember, generic methods must be proved by the compiler to work for all possible constructions, not just the constructions you make. Generics are not templates.
The error message could be more clear, I agree. I apologize for the poor experience. Improving that error was on my list, and I never got to it.
You should put class
constraints on your generic type parameters, and then it will work as you expect.
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