Geeks With Blogs
Under The Influence(of code) Abhijeet Patel's blog

C# 4.0 introduces the notion of Covariance and Contravariance of generic type parameters for interfaces and delegate types. Eric Lippert has put together a bunch of posts that goes into details of the why and how, an excellent read but not for the faint of heart. I would strongly suggest reading these posts to get a firm grounding and a better appreciation of this feature. It took me a while to get my head wrapped around this, especially since none of the VS2010 Betas were not out at the time and there was no way to try out any code. Now that I have Beta2 of VS2010 at hand I decided to try out some code samples first hand and solidify my understanding of the feature.

Suppose we have the following inheritance hierarchy:

 
  1. interface IAnimal  
  2. {  
  3.     void Speak();  
  4. }  
  5.   
  6. class Whale : IAnimal  
  7. {  
  8.      public void  Speak()  
  9.     {  
  10.          Console.WriteLine("I'm a Whale");  
  11.     }  
  12. }  
  13.   
  14. class Giraffe : IAnimal  
  15. {  
  16.     public  void Speak()  
  17.     {  
  18.         Console.WriteLine("I'm a Giraffe");  
  19.     }  
  20. }  

 

and a processor to process animals:

 
  1. interface IProcessor<T>  
  2. {  
  3.     void Process(IEnumerable<T> ts);     
  4. }  
  5.   
  6. class Processor<T> : IProcessor<T> where T:IAnimal  
  7. {  
  8.     public void Process(IEnumerable<T> ts)  
  9.     {  
  10.         foreach (var t in ts)  
  11.         {  
  12.             t.Speak();  
  13.         }  
  14.     }          
  15. }  

 

It would be natural to be able to create a Processor to process any kind of animal, but the following code does not compile today on the C# 3.0 compiler

 
  1. List<Giraffe> giraffeList = new List<Giraffe> { new Giraffe() };  
  2. IProcessor<IAnimal> proc = new Processor<IAnimal>();  
  3. proc.Process(giraffeList);  
We have to add a silly"OfType<IAnimal>" cast to make it work:
 
  1. List<Giraffe> giraffeList = new List<Giraffe> { new Giraffe() };  
  2. IProcessor<IAnimal> proc = new Processor<IAnimal>();  
  3. proc.Process(giraffeList.OfType<IAnimal>());  

 

In C# 4.0 no such cast is needed and everything just works as expected. Why? because in .NET 4.0 IEnumerable<T> is covariant in it's type parameter T, this is what the definition looks like

 
  1. public interface IEnumerable<out T> : IEnumerable  
  2. {  
  3. ....  
  4. ....  
  5. }   
The "out" keyword is what makes the "T" covariant; which means that you can pass in any type which is more derived than "T" as the type argument, hence we can pass in IEnumerable<Giraffe> or IEnumerable<Whale> to the Process method which accepts an IEnumerable<IAnimal> and everything just works as expected in a type safe way.

 

You can define your own covariant types to allow for such conversions.
Consider the following generic class definition to create instances of a specified type
 
  1. interface IFactory<out T>  
  2. {  
  3.     T CreateInstance();  
  4. }  
  5.   
  6. class Factory<T> : IFactory<T> where T: new()  
  7. {  
  8.     public T CreateInstance()  
  9.     {  
  10.         return new T();  
  11.     }          
  12. }  
IFactory<T> is covariant in T so we can treat a Factory<Giraffe> as an IFactory<IAnimal>. This allows us to treat factories of types in the same inheritance chain uniformly like so:
 
  1. IFactory<IAnimal> giraffeFactory = new Factory<Giraffe>();  
  2. IFactory<IAnimal> whaleFactory = new Factory<Whale>();  
  3.   
  4. List<IFactory<IAnimal>> factories = new List<IFactory<IAnimal>>{ giraffeFactory, whaleFactory };  
  5. foreach (var factory in factories)  
  6. {  
  7.     factory.CreateInstance().Speak();  
  8. }  

Note that it is illegal for a covariant type parameter to occur in an input position, it can only occur in output positions such as return type of a method in the type. This restriction is key in making covariance work,allowing the type parameter in an input position would break covariance. How you might ask. Imagine if the following illegal code were legal and you could specify T in an input position.

 
  1. interface IFactory<out T>    
  2. {  
  3.     T CreateInstance();  
  4.     T CreateInstanceFrom(T t);  
  5. }  
  6.   
  7. class Factory<T> : IFactory<T>  
  8.     where T : new()  
  9.     where T : ICloneable  
  10. {  
  11.     public T CreateInstance()  
  12.     {  
  13.         return new T();  
  14.     }  
  15.     public T CreateInstanceFrom(T t)  
  16.     {  
  17.         return (T)t.Clone();  
  18.     }  
  19.   
  20. }  
We added a constraint to the factory class to constrain the type parameter to support ICloneable and added a new method CreateInstanceFrom which accept the instance to clone. Now what happens if we do the following:
 
  1. IFactory<IAnimal> giraffeFactory = new Factory<Giraffe>();  
  2. giraffeFactory.CreateInstanceFrom(new Whale())  
Oh my! we just passed in a Whale to clone a Giraffe, and we'd bomb a runtime.
It is precisely for this reason that a covariant type parameter can only occur in output positions(hence the keyword out) and the runtime error is now a compile time error.

 

But what if we wanted to have the type paramter in an input position to get the benefits variance has to offer, enter contrvariance which is the dual of covariance. In that a type argument can be safely replaced by a more derived type but only in an input position.

If T appears ONLY in an input position (such as a parameter in methods of IProcessor) then we can mark T as contravariant using the in keyword, allowing us to substitute any sub type of T.

 
  1. interface IProcessor<in T>  
  2. {  
  3.     void Process(IEnumerable<T> ts);  
  4. }  

This makes assignments such as the following legal.

 
  1. List<Giraffe> giraffes = new List<Giraffe> { new Giraffe() };  
  2. List<Whale> whales = new List<Whale> { new Whale() };  
  3. IProcessor<IAnimal> animalProc = new Processor<IAnimal>();  
  4. IProcessor<Giraffe> giraffeProcessor = animalProc;  
  5. IProcessor<Whale> whaleProcessor = animalProc;  
  6. giraffeProcessor.Process(giraffes);  
  7. whaleProcessor.Process(whales);  

 

Finally let's look at variance in delegate types. Consider the following delegate definition and it's subsequent usage

 
  1. public delegate void MyAction<in T>(T t);  
  2. ........  
  3. .......  
  4. .........  
  5.  MyAction<IAnimal> animalAction = a => a.Speak();  
Since T is contravariant, we can treat MyAction<IAnimal> as MyAction<Giraffe> since Giraffe is more derived than IAnimal
  1. MyAction<Giraffe> rafAction = animalAction;  

 

Now let's combine both co and contravariance, consider the following delegate

 
  1. public delegate T2 MyFunc<in T1,out T2>(T1 t1);  
T2 is covariant and T1 is contravariant and hence we can use T1 or any subtypes of T1 as the first generic type paramter and T2 or any of its base types as the second generic type parameter, allowing us to use assignments such as the following with full type safety
 
  1. Func<IAnimal, Giraffe> func = a => a as Giraffe;  
  2. Func<Giraffe, IAnimal> funcG = func;  

 

Covariance and Contravariance may sound like a mouthful and daunting to understand but at its core it's quite simple to grasp once you understand its purpose, it enables writing generic code more naturally and allows safe type coercions. That's it for today. Happy coding.

Posted on Sunday, January 10, 2010 12:50 PM | Back to top


Comments on this post: Covariance and Contravariance in C# 4.0

# re: Covariance and Contravariance in C# 4.0
Requesting Gravatar...
Awesome, concise explanation and one of the best I've seen so far explaining covariance and contravariance.
Left by Bill Craun on Jan 16, 2010 7:02 AM

# re: Covariance and Contravariance in C# 4.0
Requesting Gravatar...
A good explanation, but I think the whole Covariance and Contravariance concept will be ignored by most developers for being too complex.
Left by Anton on Jan 16, 2010 8:11 PM

# re: Covariance and Contravariance in C# 4.0
Requesting Gravatar...
Very nice post.
Most articles I've read so far have been far too complicated but this one is straight forward and concise.
Left by Jay on Mar 19, 2010 2:50 PM

# re: Covariance and Contravariance in C# 4.0
Requesting Gravatar...
cool 1!!!!!!!!
Left by SHRUTI on Apr 27, 2010 2:37 AM

# re: Covariance and Contravariance in C# 4.0
Requesting Gravatar...
yes this is cool but i find this link better:
http://www.aspboy.com/Categories/CSharpArticles/DelegateCovarianceAndContravarianceExplained.aspx
Left by Rajesh on Jun 17, 2010 11:21 PM

# re: Covariance and Contravariance in C# 4.0
Requesting Gravatar...
Very good explanation
Left by Shweta on Sep 04, 2010 9:47 AM

# re: Covariance and Contravariance in C# 4.0
Requesting Gravatar...
A Nice blog. Simple to understand
Left by Mario on Aug 11, 2011 9:38 PM

# Covariance and Contravariance in C# 4.0
Requesting Gravatar...
I admire good writing but lazy enough to write comment. Your writing compelled me to say 'Boy ..pat on your back'...its really good ...keep the good work up.
Left by Sattha on Oct 13, 2011 9:30 PM

# re: Covariance and Contravariance in C# 4.0
Requesting Gravatar...
Wow... nicest explanation about covariance and contravariance... cloning whale to giraffe had me laughing my lungs out.
Left by Aliyu on Feb 01, 2012 7:38 AM

# re: Covariance and Contravariance in C# 4.0
Requesting Gravatar...
Way to go. I was actually spinning my head to understand this concept w.r.t.o real world scenario, But ur explanation here is just wat the doctor ordered for me. Thanks mate. Keep up the good work
Left by Fazil on May 31, 2012 10:01 AM

Your comment:
 (will show your gravatar)


Copyright © Abhijeet Patel | Powered by: GeeksWithBlogs.net | Join free