C# variance : The Co and Contra variance explained

by silvercrux @ 9. August 2010 23:07

This topic can be ideally explained with a simple and pragmatic 2 part Q and A.

What is Covariance and Contra variance in C# 4 ?

This can be best explained by an example. If you image there are 2 types : x and y, then exactly one of the statement is true :

1) x < y
2) x > y
3) x = y
4) x not related to y (Both are totally different having their own chain)

Memory locations in C# all have a type associated with them. At runtime you can store an object which is an instance of an equal or smaller type in that memory location.

So considering that :
Covariant : Is converting from narrower to a wider type
contra variant : Is Conversion from wider to a narrower type

Covariance basically means that the return value of a method that is referenced by your delegate can have a different return type than that specified by the delegate itself, so long as the return type of the method is a subclass of the return type of the delegate. Basically this means from going to a wider type from a narrower type.

 

Why do you care about Co and Contra variance ?

Prior to C# 4.0, language was not capable of working with co and contra variance conversion. Now it does support. So the question is why you might need them ? Well the answer is simple, now Inheritance hierarchy works across Generic types, thanks to Covariance and Contra variance support in C# 4.0.

Let's take an example (This would not work in versions prior to C# 4.0) :

Covariant

   1: class Fruit { }
   2:     class Apple : Fruit { } 
   3:  
   4:     class Program
   5:     {
   6:         delegate T MyFunc<out T>();
   7:         static void Main(string[] args)
   8:         {
   9:              MyFunc<Apple> apple1 = () => new Apple();
  10:              MyFunc<Fruit> fruit = apple1;
  11:         }
  12:     } 
  13:  

In the above code we are assigning a narrower type to a wider type. Notice the use of the word "out" in Delegate MyFunc definition

Contravariant

   1: class Fruit { }
   2:     class Apple : Fruit { } 
   3:  
   4:     class Program
   5:     {
   6:         delegate void MyAction<in T>(T a);
   7:         static void Main(string[] args)
   8:         {
   9:              MyFunc<Fruit> fruit = () => new Fruit();
  10:              MyFunc<Apple> apple1 = fruit;
  11:         }
  12:     }

In the above code we are assigning a narrower type to a wider type. Notice the use of the word "in" in Delegate MyFunc definition

I hope this clears the confusion about Co and Contra-variance. If it's still not clear we can talk via comments :)





Comments are closed

© Copyright SilverCrux Technologies. All rights reserved. We provide website html services.