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


