Friday, 29 April 2011

Covariance and contravariance in c-sharp 4

Wow. They are some scary fucking words. The brilliant Jon Skeet does a great job of describing what they are all about in his tekpub video, but you need to pay for access ( well worth doing btw). Im going to try my best to describe what they are and how they can be useful.

Normally when we declare an interface or a class, we would do something like this:

 interface IFoo <T> {  
    T GiveMeSomething()  
    void TakeSomething(T instance)   

This can provide us with restrictions though. Say we have the following class declarations:

 class Fruit { }  
 class Apple : Fruit {}  
 class Banana : Fruit {}  

One would expect that you could do something like this:

 List<Fruit> fruitBowl = new List<Fruit>();  
 fruitBowl.Add(new Apple());  

Obviously, we can not without casting it down to it's base type. The reason for this, is that we can read and write to the list. I could request an item for the list, but I don't really know what type it would be as I could add any type of fruit. This goes against the whole point of a strongly typed List. So naturally, we get a compile error.

Covariance is all to do with values coming out of an interface/class. Effectively making it read only.

 interface IFoo <out T> {  
    T GiveMeSomething()  

Covariance is effectively saying you can not modify this object, only take from it, hence the out parameter. The famous example being IEnumerbale<T>.

Contravariance is when values only go into a class/interface. Effectively making it write only.

 interface IFoo <in T> {  
    void TakeSomething(T instance)  

This is nice as it allows us to pass in any type of fruit, without having to cast it down to the base class. So I can do the following:

 IFoo<Fruit> fruitbowl = new Foo<Fruit>();  
 fruitBowl.TakeSomething(new Apple());  
 fruitBowl.TakeSomething(new Banana());  
 fruitBowl.TakeSomething(new Apple());  

1 comment:

  1. What is the benefit of using these parameter specifications on the interface - out and in?