C# Generics T, in T, out T parameter

Petr Kostelanský | 9 November 2017
I am going to show you differences between covariance, contravariance and invariance in generics in C#.

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>.

 

Loading ads...