Skip to main content
A covariant type parameter in C# allows a generic interface or delegate to use a more derived (specific) type than the one specified by the generic type argument. Denoted by the out contextual keyword, covariance preserves the assignment compatibility of generic types, enabling implicit reference conversions from Generic<Derived> to Generic<Base>.

Syntax and Declaration

Covariance is applied at the type parameter declaration level using the out modifier. It is strictly limited to generic interfaces and generic delegates.

Structural Mechanics

When a type parameter T is marked as covariant (out T), the C# compiler enforces strict positional validation. The type T is restricted to output positions only. However, the definition of an output position includes mathematical resolution of nested variance. Permitted (Output Positions):
  • Method return types.
  • get accessors of properties.
  • Nested within contravariant types in input positions: A covariant type parameter can appear in a method parameter list if it is nested inside a contravariant generic type (e.g., Action<T>). Because an input position of an input position mathematically resolves to an output position, this is structurally valid.
Prohibited (Input Positions):
  • Direct method parameters (e.g., void Consume(T item)).
  • set or init accessors of properties.
  • Generic constraints on methods within the interface.
  • ref, in, or out method parameters.
  • Nested within covariant types in input positions (e.g., void Consume(IEnumerable<T> items)).
If a covariant type parameter resolves to an input position, the compiler generates error CS1961.

Type System Rules and Constraints

  1. Reference Types Only: Covariance in C# is supported exclusively for reference types. It relies on implicit reference conversions at the CLR level. Value types (structs, enums, primitives like int or double) have different memory layouts and cannot be variant.
  1. Interface and Delegate Exclusivity: The out modifier cannot be applied to type parameters of classes or structs. Variance is a feature of the abstraction (interfaces/delegates), not the implementation.
  1. Method Overloading: While covariant type parameters cannot be used as direct method parameters, their ability to be nested within contravariant types means they can be used to differentiate method overloads. The compiler successfully resolves overloads based on the distinct contravariant delegate or interface signatures wrapping the covariant type parameter.

Assignment Compatibility

The primary mechanical effect of a covariant type parameter is altering the compiler’s type-checking rules for assignment. If TypeD derives from TypeB, and I<out T> is covariant, then I<TypeD> is considered a subtype of I<TypeB>.
Tired of Poor C# Skills? Fix That With Deep Grasping!Learn More