The following raises complaints:
interface IInvariant<TInv> {}
interface ICovariant<out TCov> {
IInvariant<TCov> M(); // The covariant type parameter `TCov'
// must be invariantly valid on
// `ICovariant<TCov>.M()'
}
interface IContravariant<in TCon> {
void M(IInvariant<TCon> v); // The contravariant type parameter
// `TCon' must be invariantly valid
// on `IContravariant<TCon>.M()'
}
but I can't imagine where this wouldn't be type-safe. (snip*) Is this the reason why this is disallowed, or is there some other case which violates type safety which I'm not aware of?
* My initial thoughts were admittedly convoluted, but despite this, the responses are very thorough, and @Theodoros Chatzigiannakis even dissected my initial assumptions with impressive accuracy.
Alongside a good slap from retrospect, I realize that I had falsely assumed that the type signature of ICovariant::M
remains a Func<IInvariant<Derived>>
when its ICovariant<Derived>
is assigned to a ICovariant<Base>
. Then, assigning that M
to Func<IInvariant<Base>>
would look fine coming from an ICovariant<Base>
, but would of course be illegal. Why not just ban this last, obviously-illegal cast? (so I thought)
I feel this false and tangential guess detracts from the question, as Eric Lippert also points out, but for historical purposes, the snipped part:
The most intuitive explanation to me is that, taking
ICovariant
as an example, the covariantTCov
implies that the methodIInvariant<TCov> M()
could be cast to someIInvariant<TSuper> M()
whereTSuper super TCov
, which violates the invariance ofTInv
inIInvariant
. However, this implication doesn't seem necessary: the invariance ofIInvariant
onTInv
could easily be enforced by disallowing the cast ofM
.
A generic interface or generic delegate type can have both covariant and contravariant type parameters. Variance applies only to reference types; if you specify a value type for a variant type parameter, that type parameter is invariant for the resulting constructed type.
In short, type variance describes the types that may be substituted in place of another type. Covariance: We say a substitution is covariant with a type, Foo, if Foo or any other class with a subclass relationship to Foo is valid.
Covariance: Covariance lets you to pass a derived type object where a base type object is expected Covariance can be applied on delegate, generic, array, interface, etc. Contravariance: Contravariance is applied to parameters.
Variance is the interconnection of Sub-Typing relationships which are either of complicated types or of their constituent types. Variance explains inheritance correlation of Types that have parameters or arguments within them. These types belongs to the generic classes, which takes a type like a parameter.
Let's look at a more concrete example. We'll make a couple implementations of these interfaces:
class InvariantImpl<T> : IInvariant<T>
{
}
class CovariantImpl<T> : ICovariant<T>
{
public IInvariant<T> M()
{
return new InvariantImpl<T>();
}
}
Now, let's assume that the compiler didn't complain about this and try to use it in a simple way:
static IInvariant<object> Foo( ICovariant<object> o )
{
return o.M();
}
So far so good. o
is ICovariant<object>
and that interface guarantees that we have a method that can return an IInvariant<object>
. We don't have to perform any casts or conversions here, everything is fine. Now let's call the method:
var x = Foo( new CovariantImpl<string>() );
Because ICovariant
is covariant, this is a valid method call, we can substitute an ICovariant<string>
wherever something wants an ICovariant<object>
because of that covariance.
But we have a problem. Inside Foo
, we call ICovariant<object>.M()
and expect it to return an IInvariant<object>
because that's what the ICovariant
interface says it will do. But it can't do that, because the actual implementation we've passed actually implements ICovariant<string>
and its M
method returns IInvariant<string>
, which has nothing to do with IInvariant<object>
due to the invariance of that interface. They are completely different types.
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