New features in C # 4.0. Part 3: Covariance of generalizations

Original author: Justin Etheredge
  • Transfer
When generic came to us with C # 2.0, they became one of the best features in this language. Those who have ever created strongly typed collection classes in C # 1.0 know how much they simplified our lives and reduced the amount of code. The only problem was that generic types did not follow the same inheritance rules that were valid for regular types.

Let's start with the definition of two simple classes that we will use in this article:
  1. public class Shape
  2. {
  3. }
  4.  
  5. public class Circle : Shape
  6. {
  7. }
* This source code was highlighted with Source Code Highlighter.
This is a classic class hierarchy that does nothing concrete yet, but we don’t need it now. Now we define a dummy class, which can be a container for any type.
  1. public interface IContainer
  2. {
  3.   T GetItem();
  4. }
  5.  
  6. public class Container: IContainer
  7. {
  8.   private T item;
  9.  
  10.   public Container(T item)
  11.   {
  12.     this.item = item;
  13.   }
  14.  
  15.   public T GetItem()
  16.   {
  17.     return item;
  18.   }
  19. }
* This source code was highlighted with Source Code Highlighter.
We have a hierarchy and a container. Now let's look at what we can not do in the current version of C # - 3.0.
  1. static void Main(string[] args)
  2. {      
  3.   IContainer list = GetList();
  4. }
  5.  
  6. public static IContainer GetList()
  7. {
  8.   return new Container(new Circle());
  9. }
* This source code was highlighted with Source Code Highlighter.
We have a GetList method whose return type is defined as IContainer, and it returns Container. Since Circle inherits from Shape, and Container implements the IContainer interface, it might seem that this will work. But, you guessed it, C # 3.0 is not capable of this.

In C # 4.0, we have a way to make this work - we just need to add the out keyword to the type parameter in our IContainer interface definition (I note that covariance in C # 4.0 is limited to interfaces and delegates).
  1. public interface IContainer
  2. {
  3.   T GetItem();
  4. }
* This source code was highlighted with Source Code Highlighter.
This construction tells the compiler that type T is covariant, which means that any IContainer will accept any type equivalent or more specific than T. As we saw above, the return type was IContainer, but if we put the out parameter to our interface, then we can easily return IContainer. So why is it decided to use the out keyword? That's because when you define a type parameter as covariant, you can only return this type from the interface. For example, such a construction is not allowed:
  1. public interface IContainer
  2. {
  3.   void SetItem(T item);
  4.   T GetItem();
  5. }
* This source code was highlighted with Source Code Highlighter.
But why won't it work? The answer is actually very simple - type safety. Let's look at the consequences of what we have done:
  1. static void Main(string[] args)
  2. {
  3.   IContainer container = new Container();
  4.   SetItem(container);
  5. }
  6.  
  7. public static void SetItem(IContainer container)
  8. {
  9.   container.SetItem(new Square()); // BOOM!!!
  10. }
* This source code was highlighted with Source Code Highlighter.
Since T is covariant and, therefore, we can assign a Container to a variable of type IContainerpassing it further to our static SetItem method, which takes an IContainer type parameterand then we take this parameter and try to add a Square type variable to it. Everything seems to be right - IContainer parameter typeand that gives us the right to add Square to it. Wrong. This expression will “explode” as we try to add Square to the container that holds the Circle. Therefore, with the out keyword, they limited covariance to only one direction.

You are probably wondering how all this is implemented in CLR 4.0? There is no need for implementation. Generic types worked in CLR 2.0 and they already allowed similar behavior. Since C # tried to maintain type safety, we were not allowed to do this, but the CLR handles this at a time. And, a little remark - arrays in C # allow this behavior, so give it a try. I hope you enjoyed this article and the new one in this series is just around the corner!

Translation of C # 4.0 article New Features Part 3 - Generic Covariance

Crosspost from my blog


Also popular now: