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 :)