Click here to Skip to main content
15,867,453 members
Articles / Desktop Programming / Windows Forms

Covariance, Contravariance and Invariance in C# Language

Rate me:
Please Sign up or sign in to vote.
3.22/5 (10 votes)
23 Jul 2009Ms-PL3 min read 38.2K   12   7
Covariance, contravariance and invariance in C# language

Introduction

The newest version of C# language - 4.0 - will be shipped with a couple of interesting features. One of them is to allow covariance and contravariance on parameterized delegate and interface types. Sounds very mysterious, isn't it?

There are many great developers that didn't hear about any kind of “-variance” (although they are using it unwittingly very often). This has much to do with inheritance – in fact many people think that this is simply just “another thing” offered by generalization. So why bother about this extra knowledge? Because C# language's operators are sometimes covariant, sometimes contravariant and sometimes invariant. Knowing principles of these is imperative in order to avoid compile errors, nasty runtime exceptions and be able to use smarter programming techniques.

This article provides a brief description of co- and contravariance in C# programming language. The first part introduces some key theoretical concepts and notions. The second part focuses on exploitation of this technique in C# language.

Theory

Let's begin with the most boring (but necessary) part – theory. An operator between types is said to be covariant if it orders these types from more specific to more general ones. Similarly an operator between types is said to be contravariant if it orders them in the reversed order. Whenever neither of these conditions is met, an operator is said to be invariant.

Keeping in mind that these definitions probably didn't explain much to you (however the opposite may be true), let's get to some real-live examples.

Usage

These concepts are commonly used in many object-oriented programming languages (including C#, C++ and Java) to allow high degree of flexibility. This flexibility can be achieved by replacing the original type of data by type that derives from it (contravariance) or by type from which the given type derives (covariance).

Both of these will be shown in the example. Suppose we have the following class model...

... and that the following method has been defined:

C#
public Class2 Method(Class2 argument)
{
return null;
}

Covariance

In C#, return value of every method is covariant, while all arguments are contravariant. This means that the value returned by this method may be pointed by a reference of type C2, but also by reference of any type from which C2 derives (directly or not). As a result, both of the following assignments are correct:

C#
Class2 = Method(null);Class1 = Method(null); 

Assigning return value of this method to a reference of type Class3 will result in compile error, because the return value operator is not contravariant.

Contravariance

However all arguments of every method are contravariant (but not covariant). This means that instead of passing the object of type required by the method's signature, you are also allowed to pass object of every type that derives from it. So let's get back to the previous example. Both of the following lines are proper:

C#
Method(new Class2());
Method(new Class3()); 

Similarly to the previous example, calling this method with an argument of type Class1 will get you compile error as method's arguments are not covariant.

Collections

Collections are quite noteworthy cases regarding co- and contravariance because they are treated differently. Type safety forces them to be invariant. This means that only object of type int[] (precisely) may be assigned to the following reference:

C#
int[] array; 

Seems quite harsh, huh? Indeed it is. That's why Microsoft allowed all arrays of reference types to be covariant. This means that (getting back to the previous example) you are allowed to make this assignment:

C#
Class1[] generalizedArray;Class2[] specializedArray = new Class2[]{new Class2()}; 

Seems comfortable? It really is, but this solution has a major drawback as well. The compiler won't catch the following error:

C#
generalizedArray[0] = new C1(); 

The compiler won't even issue a warning. You will get a nasty ArrayTypeMismatchException at runtime instead. Quite painful.

Conclusion

This article introduced elementary notions and ideas that stand behind covariance, contravariance and invariance. Usage of these in C# programming language along with a couple of simple examples has also been presented.

Co- and contravariance are interesting concepts. Having at least some basic knowledge about them could prove to be very useful, especially nowadays with C# 4.0 being at the door, as it will use them quite heavily.

History

  • 23rd July, 2009: Initial post

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)


Written By
CEO
Solomon Islands Solomon Islands
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 2 Pin
Prasaad SJ8-Jan-15 18:12
Prasaad SJ8-Jan-15 18:12 
GeneralMy vote of 5 Pin
Verbiest Kristof28-Jul-09 1:18
Verbiest Kristof28-Jul-09 1:18 
GeneralMy vote of 2 Pin
Emile van Gerwen27-Jul-09 23:14
Emile van Gerwen27-Jul-09 23:14 
GeneralMy vote of 1 Pin
ArchAngel12327-Jul-09 17:50
ArchAngel12327-Jul-09 17:50 
Generalcool Pin
Mubi | www.mrmubi.com23-Jul-09 7:39
professionalMubi | www.mrmubi.com23-Jul-09 7:39 
Generaltwo comments [modified] Pin
fhrdina23-Jul-09 2:56
fhrdina23-Jul-09 2:56 
GeneralRe: two comments Pin
fhrdina23-Jul-09 6:29
fhrdina23-Jul-09 6:29 

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

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