Let's begin with some definitions because this one is confusing (liberated from the original Microsoft docs).
Covariance enables you to use a more derived type than originally specified. You can assign an instance of IEnumerable<Derived>
to a variable of type IEnumerable<Base>
.
Contravariance enables you to use a more generic (less derived) type than originally specified.
You can assign an instance of Action<Base>
to a variable of type Action<Derived>
.
Invariance means that you can use only the type originally specified. An
invariant generic type parameter is neither covariant nor contravariant.
You cannot assign an instance of List<Base>
to a variable of type List<Derived>
or vice versa.
The covariance examples are pretty much what you are used to in the real world. You can make assignment the look like ordinary polymorphism (Microsoft's words not mine).
// Assignment compatibility. string str = "test"; // An object of a more derived type is assigned to an object of a less derived type. object obj = str; // Covariance. IEnumerable<string> strings = new List<string>(); // An object that is instantiated with a more derived type argument // is assigned to an object instantiated with a less derived type argument. // Assignment compatibility is preserved. IEnumerable<object> objects = strings;
Covariance makes sense when doing assignments. You can always cast down. Covariant return type now makes sense. This feature allows you to return a more derived type.
class Compilation { public virtual Compilation WithOptions(Options options)... } class CSharpCompilation : Compilation { public override CSharpCompilation WithOptions(Options options)... }
The only problem is I don't know when I would ever use this. Maybe I am just too used to naming the method something different.