Let's take a look at the examples how those parameters are differ.
Invariant <T>
IList<Cat> cats = new List<Cat>(); // OK IList<Animal> animals = cats; // Error: Cannot implicitly convert type 'System.Collections.Generic.IList<CovarianceContravariance.Cat>' to 'System.Collections.Generic.IList<CovarianceContravariance.Animal>'.An explicit conversion exists (are you missing a cast?)
Explanation: IList<T> is invariant on T, Neither IList<Cat> nor IList<Animal> is a subtype of the other.
Covariant <out T>
IEnumerable<Cat> cats = new List<Cat>(); IEnumerable<Animal> animals = catCol;
Explanation: IEnumerable<out T> is covariant on T, because IEnumerable<Cat> is a subtype of IEnumerable<Animal>.
Contravariant <in T>
IComparer<Animal> animalComparer = new AnimalComparer(); IComparer<Cat> catComparer = animalComparer;
Explanation: IComparer<in T> is contravariant on T, because IComparer<Cat> is a reversed subtype of IComparer<Animal>.
There is real example how could be contravariance used:
public class Animal { public int Weight { get; set; } } public class Cat : Animal { public Color HairColor { get; set; } } public class AnimalComparer : IComparer<Animal> { public int Compare(Animal x, Animal y) { if (x.Weight == y.Weight) return 0; else if (x.Weight > y.Weight) return 1; return -1; } }
AnimalComparer animalComparer = new AnimalComparer(); List<Cat> cats = new List<Cat>(); cats.Sort(animalComparer); // Sort expect IComparer<Cat> parameter
Thanks to IComparer<in T> interface we can use class that inherit from IComparer<Animal> in Sort method that expect input parameter IComparer<Cat>.