Click here to Skip to main content
Click here to Skip to main content

Understanding Covariance and Contravariance in .NET 4.0

, 12 Sep 2011
Rate this:
Please Sign up or sign in to vote.
A covariant type parameter can be used as the return type of a generic delegate/interface, and contravariant type parameters can be used as input parameter types.

A couple of years back, while working on a project, I got a compilation exception related to Generics.

I had a base class BaseVO and classes deriving from BaseVO. For example, “FlightVO : BaseVO”.My idealistic assumption was that since: “FlightVO” inherits from BaseVO, and since BaseVO b = new FlightVO() is valid as per inheritance, then:

“List<BaseVO> lstBaseVO = new List<FlightVO>() would also be valid.”

But duh !!! Instead, I get a compilation exception that says:

Argument ’1′: cannot convert from 
  ‘System.Collections.Generic.List<ValueObjects.FlightVO>’ 
  to ‘System.Collections.Generic.List<ValueObjects.BaseVO>’

Since I was so sure about my assumption being right, I posted it on the MSDN forum just to confirm that it was a “bug” Smile | :) in Generics. Thread in MSDN forum: http://social.msdn.microsoft.com/Forums/bg-BG/netfxbcl/thread/e4863bda-3f24-4a28-af55-230baaff5e7b.

Those were the .NET 3.0 days and one of the MVP moderators gave a pretty good explanation as to why my assumption was wrong. Here’s his explanation:

List<Base> and List<Derived> are distinct types, you can’t convert List<Derived> to List<Base>. If it were possible, you could pass a List<Derived> to a method accepting List<Base> and that method could add an object of type Base to the list, which violates the promise that the original list only contains objects of type Derived”.

I assume someone in the CLR team might have found my assumption quite viable and it got shape in .NET 4.0 as “Covariance and Contravariance in Generics” :-).

So the above case is a valid example where “Covariance in Generics” can be used.

However, if we compile List<BaseVO> lstBaseVO = new List<FlightVO>() in .NET 4.0, it would still give the same compilation error for the very explanation given by our MVP friend. In order to make it covariant compliant, our modified piece of code would look like:

IEnumerable< BaseVO > lstBaseVO = new List< FlightVO >();

The above statement is valid because, in .NET 4.0, List<T> : IEnumerable<out T> where type ‘T’ is marked as covariant.

Here’s a quick summarization about variance in Generics.

  1. What are the programming constructs where covariance and contravariance are applicable? Variant type parameters are restricted to generic interfaces and generic delegate types thereby ensuring type safety.
  2. In what scenarios would we want to use variance in Generics? Let's look at a code snippet.

covariance example

So, covariance and contravariance of generic type parameters enable us to use constructed generic types [A<Athlete>] whose type arguments [Athlete] are more derived (covariance) or less derived (contravariance) than the type argument [Person] of a target constructed type [ISample<Person>].

Covariance seems very natural because it behaves like polymorphism, whereas Contravariance seems counterintuitive. One thing that we need to be clear about is that both Covariance and Contravariance both adhere to the thumb rule of “Inheritance” which says,

“A derived class instance can be assigned to a base class variable”

Now, let’s look at a Contravariance example.

contravariiance1

Line 2 of code snippet might make you feel that the thumb rule of inheritance is being violated. I.e., the derived class variable d is being assigned an instance of the Base class.

However, that isn’t the case. Let’s understand each line of the code snippet.

contravariiance2

contravariiance4

contravariiance3

In general, a covariant type parameter can be used as the return type of a generic delegate/interface, and contravariant type parameters can be used as input parameter types.

A brief summary of facts about variance in the common language runtime:

  1. A generic interface or generic delegate type can have both covariant and contravariant type parameters.
  2. Variance applies only to reference types; if you specify a value type for a variant type parameter, that type parameter is invariant for the resulting constructed type.
  3. How do we define variant generic interfaces and delegates?

Starting with the .NET Framework 4, Visual Basic and C# have keywords that enable you to mark the generic type parameters of interfaces and delegates as covariant or contravariant.

  • A covariant type parameter is marked with the out keyword. You cannot use a covariant type parameter as a generic type constraint for interface methods. E.g., T Display<out T>(); is an invalid statement.
  • A contravariant type parameter is marked with the in keyword. You can use a contravariant type parameter as a generic type constraint for an interface method.

Thank you.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

girijaa
Technical Lead Mastek
India India
A Software professional with expertise in developing applications using Microsoft Stack [.Net 2.0/3.0/3.5/4.0] , SQL Server 2005/2008 , Asp.Net [Webforms,MVC] , Entity Framework , WPF , Winforms , Enterprise Library .
Knowledge of Sharepoint [MOSS 2007] and SQL Server BI and still learning ...
Follow on   Twitter

Comments and Discussions

 
GeneralMy vote of 5 PinmemberRenju Vinod17-Dec-12 0:51 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140709.1 | Last Updated 12 Sep 2011
Article Copyright 2011 by girijaa
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid