joydip_kanjilal
Contributor

How to use covariance and contravariance in C#

how-to
Jun 10, 20164 mins
C#Development Libraries and FrameworksMicrosoft .NET

You can use covariance and contravariance to provide polymorphic extension to delegates, arrays, and generics in C#. Here’s how.

two hands holding puzzle pieces

The C# programming language provides support for variance in two ways: covariance and contravariance. Covariance and contravariance are supported only for reference types. Since C# 4.0, you can specify in and out parameters on generic types as well.

Covariance and contravariance are features added with C# 4.0 that provide polymorphic extension to delegates, arrays and even generics. As MSDN states:

Covariance and contravariance are terms that refer to the ability to use a less derived (less specific) or more derived type (more specific) than originally specified. Generic type parameters support covariance and contravariance to provide greater flexibility in assigning and using generic types.

While covariance enables you to use a more derived type, contravariance allows you to take advantage of a more generic type that that was specified. By contrast, invariance enables you to use only the type that was specified originally. In essence, an invariant generic type is neither covariant nor contravariant. In C#, the support for covariance and contravariance has been extended till generic types as well. The C# programming language enables you to annotate generic type parameters using both in and out annotations. This in turn enables you to specify if they should work covariantly or contravariantly.

Generics enable you to work with data-create typesafe collections and create classes and methods that can accept a type as a parameter. You can take advantage of generics to eliminate redundant code, enforce type safety, and promote code re-usability and maintainability. It should be noted that variance is applicable only on reference types and not on value type. In essence, a conversion of a value type to a reference type is a boxing conversion and hence it is not an example of variance.

Understanding covariance in C#

Let’s now understand covariance, contravariance and invariance with code examples. First, we’ll explore covariance with a simple example. Note that while object represents the base of all types in .Net, string represents a specific type. Hence, we may say that string is covariant to object.

The following code snippet illustrates how IEnumerable is defined in C#.

public interface IEnumerable<out T> : IEnumerable
   {
       IEnumerator<T> GetEnumerator();
   }
   public interface IEnumerator<out T> : IEnumerator
   {
       bool MoveNext(); T Current { get; }
   }

So, it is evident that IEnumerable is also IEnumerable if Derived type extends the Base type. In other words, if you have two types — Derived and Base — with the former inherited from the latter, the following statement holds good:

IEnumerable<Base> obj = new List<Derived>();

Note that obj in the above code snippet, is an instance of type IEnumerable. Now, since Object is the base of all types in .Net, the following statement is also valid.

IEnumerable<Object> obj = new List<String>();

Here’s another example of covariance – this time using arrays. Refer to the code snippet given below that assigns a string array to an array instance of type object.

object[] objArray = new String[100];

Understanding contravariance in C#

Let’s now understand contravariance. Contravariance works the opposite way covariance works. Refer to the code snippet given next that illustrates the IComparer interface.

public interface IComparer<in T>
{
int Compare(T left, T right);
}

Now suppose you have a comparer that can compare two objects. You can then use the same comparer to compare two strings as well. The reason this works is that the IComparer interface in .Net is contravariant in nature and its generic type parameter is marked with the in annotation. This is an example of contravariance.

Another good example of contravariance is the Equals method that is used to compare two instances. If you have an Equals method that can compare two instances of a base type, you can use the same method to check for equality of two instances of a derived type (a type that extends the base type) as well. You can learn more on covariance and contravariance from this article.

joydip_kanjilal
Contributor

Joydip Kanjilal is a Microsoft Most Valuable Professional (MVP) in ASP.NET, as well as a speaker and the author of several books and articles. He received the prestigious MVP award for 2007, 2008, 2009, 2010, 2011, and 2012.

He has more than 20 years of experience in IT, with more than 16 years in Microsoft .Net and related technologies. He has been selected as MSDN Featured Developer of the Fortnight (MSDN) and as Community Credit Winner several times.

He is the author of eight books and more than 500 articles. Many of his articles have been featured at Microsoft’s Official Site on ASP.Net.

He was a speaker at the Spark IT 2010 event and at the Dr. Dobb’s Conference 2014 in Bangalore. He has also worked as a judge for the Jolt Awards at Dr. Dobb's Journal. He is a regular speaker at the SSWUG Virtual Conference, which is held twice each year.

More from this author