Glossary / Variance: covariance and contravariance
Variance: covariance and contravariance
Variance describes how generic type relationships change when type arguments are related by inheritance. C# uses out for covariance (a producer of T) and in for contravariance (a consumer of T) on interfaces and delegates; classes and structs remain invariant in their generic parameters.
Direction and safety
If Derived inherits from Base, covariance allows IEnumerable<Derived> to be used where IEnumerable<Base> is expected because the interface only produces values. Contravariance allows Action<Base> where Action<Derived> is expected because a function capable of handling any Base can handle a Derived. Invariance is required when a type both consumes and produces T in ways that could violate type safety.
IEnumerable<string> names = new List<string>();
IEnumerable<object> objects = names; // covariance
Action<object> logAny = Console.WriteLine;
Action<string> logText = logAny; // contravariance
Variance conversions apply only to reference types for variant generic interfaces/delegates, not value-type instantiations. Type parameters declared out cannot appear in input positions; in parameters cannot appear in output positions (subject to language rules). Arrays have historical covariance that can fail at runtime with ArrayTypeMismatchException, so prefer generic collections.
Interview questions
- What does
outvariance mean? A producer can be substituted with a more derived type argument for a base expectation. - What does
inmean? A consumer of a base type can substitute where a consumer of a derived type is required. - Are
List<Derived>andList<Base>assignable? No,List<T>is invariant. - Does variance work for value types? Variant conversions apply to reference-type arguments, not value-type instantiations.
- Why are arrays risky? Their covariance is checked at runtime and writes can fail.