New Features in C# 2010: Covariance and Contravariance and List

 

Why should you upgrade to C# 2010? After all you get most of the stuff you need done with C# 2008, and those tricky generics are still tricky in C# 2010.  Take a look at the link (all links checked on 12/14/2010) 

https://msdn.microsoft.com/en-us/library/bb383815.aspx

At the end of this blog I hope you will have a better understanding of what Covariance and Contravariance are used for. 

Covariance and Contravariance?  image

Covariance enables you to use a more derived type than that specified by the generic parameter, whereas contravariance enables you to use a less derived type. This allows for implicit conversion of classes that implement variant interfaces and provides more flexibility for matching method signatures with variant delegate types. Variant interfaces and delegates can be created by using the new in and out language keywords. The .NET Framework also introduces variance support for several existing generic interfaces and delegates, including the IEnumerable<T> interface and the Func<TResult> and Action<T> delegates. For more information, see Covariance and Contravariance (C# and Visual Basic).

 

 

 

What does it mean to use a more derived type versus a less derived type?

 

Covariant for more derived types

Covariant type parameters enable you to make assignments that look much like ordinary polymorphism.

Suppose you have a base class and a derived class, named Base and Derived. Polymorphism enables you to assign an instance of Derived to a variable of type Base.

Similarly, because the type parameter of the IEnumerable<T> interface is covariant, you can assign an instance of IEnumerable<Derived> (IEnumerable(Of Derived) in Visual Basic) to a variable of type IEnumerable<Base>, as shown in the following code.

 IEnumerable<Derived> d = new List<Derived>();
IEnumerable<Base> b = d

The List<T> class implements the IEnumerable<T> interface, so List<Derived> implements IEnumerable<Derived>. The covariant type parameter does the rest.

 

Contravariance for less derived type

Contravariance, on the other hand, seems counterintuitive. The following example creates a delegate of type Action<Base>, and then assigns that delegate to a variable of type Action<Derived>.

 

 Action<Base> b = (target) => { Console.WriteLine(target.GetType().Name); };
Action<Derived> d = b;
d(new Derived());

This seems backward, but it is type-safe code that compiles and runs.

The lambda expression matches the delegate it is assigned to, so it defines a method that takes one parameter of type Base and that has no return value. The resulting delegate can be assigned to a variable of type Action<Derived> because the type parameter T of the Action<T> delegate is contravariant. The code is type-safe because T specifies a parameter type.

When the delegate of type Action<Base> is invoked as if it were a delegate of type Action<Derived>, its argument must be of type Derived. This argument can always be passed safely to the underlying method, because the method's parameter is of type Base.

 

Conclusion:

The question now is how in the heck do you use them in real code, not sample code?  Is it really more efficient? We will see in a later blog.