Saturday, January 24, 2015

Covariance Contravariance C# simple example

In .NET 4.0 Microsoft presented covariance (out) and contravarince (in). Next image shows simple class hierarchy:


Imagine there is a need of animal container class that can only return data
interface IAnimalHub<T>
        where T: Animal
    {
        T GetAnimal();
    }
We can declare and work with animal hub type as
  IAnimalHub<Animal> hub1 = new AnimalHub<Animal>();

Can we generalise left part or, in other word, downgrade type
 IAnimalHub<Animal> hub2 = new AnimalHub<Lion>();
 IAnimalHub<Animal> hub3 = new AnimalHub<Cat>();

Definitely it is possible, because we are going only to take out data and work with it, whether it is Lion or Cat. This idea is called covariance and can be done with out keyword in C#:
interface IAnimalHub<out T>
        where T: Animal
    {
        T GetAnimal();
    }

Now what if we have Add method with some animal argument in addition to our Get method
interface IAnimalHub<out T>
        where T: Animal
    {
        T GetAnimal();

        void Add(T animal);
    }

Should it work?
No! And the reason is that otherwise it would be possible to do
IAnimalHub<Lion> hub = new AnimalHub<Lion>();
var dog = new Dog();
hub.Add(dog);

So one cannot expect possibility to Add dog while waiting Lion object. It means covariance works only with returning data (out).

What about contrvarience?

As the word says this works in the opposite to covariance. Contrvariance expects only methods where one can put/write/insert data. In C# this is presented with in keyword. Consider only Add method in our interface:
    interface IAnimalHub<in T>
        where T: Animal
    {
        void Add(T animal);
    }

 Now we can do the opposite
 
            IAnimalHub<Animal> hub1 = new AnimalHub<Animal>();
            IAnimalHub<Lion> hub2 = new AnimalHub<Animal>();
            IAnimalHub<Cat> hub3 = new AnimalHub<Animal>();
            IAnimalHub<Dog> hub4 = new AnimalHub<Animal>();

In other words we can put Lion, Cat or Dog object where we expect Animal.

Limitations

Covariance and contrvariance can be declared only in the interface or delegate, not class. Generic parameter can be only reference type, not value type.

Examples  

In .NET IEnumerable<T> only returns data, so it is created as covariant IEnumerable<out T> in version 4 of the framework. And we simply can do
Animal[] animals = new Lion[0];
Animal[] animals = new Cat[0];
IEnumerable<Animal> animals = new Lion[0];
IEnumerable<Animal> animals = new Cat[0];