To achieve type safety, it is necessary to impose additional requirements on the well-formedness of signatures of members of covariant and contravariant generic types.
This block contains only informative text.
-
Covariant parameters can only appear in "producer," "reader," or "getter" positions in the type definition; i.e., in
-
result types of methods
-
inherited interfaces
-
-
Contravariant parameters can only appear in "consumer," "writer," or "setter" positions in the type definition; i.e., in
- argument types of methods
-
NonVariant parameters can appear anywhere.
End informative text.
We now define formally what it means for a co/contravariant generic type definition to be valid.
Generic type definition: A generic type definition G<var1 T1, …, varn Tn> is valid if G is an interface or delegate type, and each of the following holds, given S = <var1 T1, …, varn Tn>, where varn is +
, -
, or nothing:
-
Every instance method and virtual method declaration is valid with respect to S
-
Every inherited interface declaration is valid with respect to S
-
There are no restrictions on static members, instance constructors, or on the type's own generic parameter constraints.
Given the annotated generic parameters S = <var1 T1, …, varn Tn>, we define what it means for various components of the type definition to be valid with respect to S. We define a negation operation on annotations, written -S, to mean "flip negatives to positives, and positives to negatives". Think of
-
"valid with respect to S" as "behaves covariantly"
-
"valid with respect to ¬S" as "behaves contravariantly"
-
"valid with respect to S and to ¬S" as "behaves non-variantly".
Note that the last of these has the effect of prohibiting covariant and contravariant parameters from a type; i.e., all generic parameters appearing shall be non-variant.
Methods. A method signature t meth(t1, …, tn) is valid with respect to S if
-
its result type signature t is valid with respect to S; and
-
each argument type signature ti is valid with respect to ¬S.
-
each method generic parameter constraint type tj is valid with respect to ¬S.
[Note: In other words, the result behaves covariantly and the arguments behave contravariantly. Constraints on generic parameters also behave contravariantly. end note]
Type signatures. A type signature t is valid with respect to S if it is
-
a non-generic type (e.g., an ordinary class or value type)
-
a generic parameter Ti for which vari is
+
or none (i.e., it is a generic parameter that is marked covariant or non-variant) -
an array type u[] and u is valid with respect to S; i.e., array types behave covariantly
-
a closed generic type G<t1,…,tn> for which each
-
ti is valid with respect to S, if the i'th parameter of G is declared covariant
-
ti is valid with respect to ¬S, if the i'th parameter of G is declared contravariant
-
ti is valid with respect to S and with respect to ¬S, if the i'th parameter of G is declared non-variant.
-