You are right that the last rule is the hardest one to understand but I assure you it is not ambiguous.
An example or two will help. Consider this type declaration:
interface I<in T, out U, V> { ... }
Is this type covariantly valid?
I<string, object, int> { }
Let's go through our definition.
To determine if it is, we examine each type argument differently, depending on whether the corresponding type parameter was declared as covariant (out), contravariant (in), or invariant (neither).
OK, so the type arguments are string
, object
and int
. The corresponding parameters are in T
, out U
and V
, respectively.
If the ith type parameter was declared as covariant (
out
), then Ti must be valid covariantly.
The second type parameter is out U
, so object
must be valid covariantly. It is.
If it was declared as contravariant (
in
), then Ti must be valid contravariantly.
The first was declared in T
, so string
must be valid contravariantly. It is.
If it was declared as invariant, then Ti must be valid invariantly.
The third V
was invariant, so int
must be valid invariantly; it must be both valid contravariantly and covariantly. It is.
We pass all three checks; the type I<string, object, int>
is valid covariantly.
OK, that one was easy.
Now let's look at a harder one.
interface IEnumerable<out W> { ... }
interface I<in T, out U, V>
{
IEnumerable<T> M();
}
IEnumerable<T>
inside I
is a type. Is IEnumerable<T>
as used inside I
valid covariantly?
Let's go through our definition. We have type argument T
corresponding to type parameter out W
. Note that T
is a type parameter of I
and a type argument of IEnumerable
.
If the ith type parameter (
W
) was declared as covariant (out
), then Ti (T
) must be valid covariantly.
OK, so for IEnumerable<T>
in I
to be valid covariantly, T
must be valid covariantly. Is it? NO. T
was declared as in T
. A type parameter that is declared in
is never valid covariantly. Therefore the type IEnumerable<T>
as used inside I
is not valid covariantly, because the "must" condition is violated.
Again, like I said in my answer to your previous question, if "valid covariantly" and "valid contravariantly" are giving you grief, just give them different names. They are well-defined formal properties; you can call them anything you want if it makes it easier for you to understand.