liveBook · Manning

Covariance

As explained in Fabulous Adventures in Data Structures and Algorithms

Covariance is a type-system property that permits a generic type instantiated with a derived reference type to be treated as the same generic type instantiated with a base reference type.

Covariance lets a generic type built from a derived reference type stand where the same generic type built from a base reference type is expected. Thus, an IEnumerable<Giraffe> can be used as an IEnumerable<Animal>, but this reasoning is safe only for suitable generic interfaces and delegates. section 2.3

The safety test is whether the abstraction can accept values of the varying type. IEnumerable<T> only reads its contents, so exposing giraffes as animals cannot insert an incompatible value. IList<T> also provides Add, making the corresponding conversion unsafe. section 2.3

C# enforces covariance through several constraints:

  • Only certain generic interfaces and delegates may be declared covariant. section 2.3
  • The varying type argument must be a reference type. section 2.3
  • A covariant type parameter appears in output positions, such as a getter’s return value, rather than as an input to mutation. section 7.2

Why IList<T> cannot be covariant

If IList<Tiger> could become IList<Animal>, calling animals.Add(new Giraffe()) would place a Giraffe into the original tiger list. Reading that element as a Tiger would violate type safety. section 2.3

A covariant immutable stack

An immutable stack can expose IImStack<out T> because its operations read values and return new stacks rather than mutating an existing stack. A stack of Tigers can therefore be assigned to IImStack<Animal>, after which a Giraffe can be pushed onto the resulting immutable stack. section 2.3

At a more abstract level, types form a category whose morphisms represent assignment compatibility. A covariant generic interface preserves this direction: relationships between types reappear among their constructed interface types. section 7.2

How Fabulous Adventures in Data Structures and Algorithms covers it

How deep it goes
Builds safety from mutation counterexamples, declaration constraints, immutable stacks, and assignment-compatibility morphisms.
Examples
Uses IEnumerable<Giraffe>, unsafe IList<Tiger> conversion, a covariant immutable stack, and a four-type assignment category.
What it assumes
Assumes familiarity with inheritance, generic interfaces, assignment compatibility, and immutable data structures.
What you can do after
Evaluate generic conversions by checking variance direction, input capabilities, reference types, and output-position usage.
How it connects
Connects covariance with generic interfaces, delegates, immutability, IEnumerable, IList, category theory, morphisms, and functors.

Also explained in

Compare how 3 books explain covariance →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Fabulous Adventures in Data Structures and Algorithms. The section links point to the parts of the book it draws on.