Covariance and contravariance deal with type conversion with arguments and return types respectively.
Covariance and contravariance deals with implicit reference conversion for array, delegate and generic types.
Covariance preserves assignment compatibility and contravariance reverses it.
Assignment compatibility means that you can assign a more specific type to a compatible less-specific type.
For example, the value of an integer variable can be stored in an object variable, like this:
int i = 25; object o = i;//ok: Assignment Compatible
|Covariance||use a more derived type than originally specified.|
|Contravariance||use a more generic (less derived) type than originally specified.|
|Invariance||use only the type originally specified.|
Covariance and contravariance are collectively known as variance.
Covariant interfaces and delegates are marked with the out keyword to indicate that values come out.
Contravariant interfaces and delegates are associated with the in keyword to indicate that values go in.
IEnumerable<T> is covariant on T, and Action<T> is contravariant on T.
The word "out" is associated with the definition of IEnumerable. We can assign IEnumerable<DerivedType> to IEnumerable<BaseType>.
This is why, we can assign IEnumerable<string> to IEnumerable<object>. Values come out from these cases (covariance).
Since Action<T> is contravariant on T, we can convert Action<object> to Action<string>. Values go into these objects (contravariance).