# Understanding Covariance and Contravariance

Monday Jul 26th 2010 by Paul Kimmel
Share:

There are as many explanations, examples, and mathematical descriptions for covariance and contravariance as you care to explore, but all of this information adds to the confusion. This article shows you why they are important concepts for software developers and how you can leverage variance when you design your code.

## Introduction

A kiss on the lips may be quite continental but diamonds are a girl's best friend.
--Jule Styn

ENIAC, short for Electronic Numerical Integrator And Computer, was the first general-purpose computer. ENIAC was developed to calculate artillery firing information. That's ironic in a way because that seemed to be what we were learning to do in the differential equations course I took at Michigan State University almost 50 years later. The ENIAC project began at the University of Pennsylvania in 1943, and I took differential equations in the early 1990s after a stint in the Army.

ENIAC is attributed to Presper Eckert and John Mauchly, but interestingly--borrowing from Wikipedia--John Von Neumann became aware of the project while working on the Manhattan Project in Los Alamos and the first test problems run were computations for the hydrogen bomb not general artillery calculations, and women were the primary programmers of this first computer. (I will have to double check these facts, but besides Wikipedia I recall this anecdotal information being relayed to me by some of MSU's great computer science teachers.) A further irony is that so few women seem to pursue computer science as a profession these days.

What was common and still seems to be common today is that mathematics and physics studies seem to breed a lot of computer scientists and ideas prevalent or introduced even now are heavily influenced by mathematics. Lambda expressions are based on Lambda Calculus, and covariance and contravariance are mathematical terms. Mathematically covariance is a measure of how much things vary together, and contravariance is the opposite of covariance. Of course, if you search the Web, the descriptions, the mathematics, and the applications of these terms can seem bewildering. For our part it is useful to know how they are applied to .NET framework. The three basic concepts discussed in this article are assignment compatibility, covariance, and contravariance. Examples are included to show you how they are applied in .NET framework 4.0.

## Understanding Assignment Compatibility

In .NET there is the concept of assignment compatibility. Assignment compatibility is the ability to assign one type to another, usually a child type to a parent type, and assignment compatibility is a big part of what makes polymorphism work.

Suppose for example you have a typical class hierarchy consisting of Person, Employee, Customer, and Manager classes. Employee and Customer inherit from Person and Manager inherits from Employee. Assignment compatibility supports the intuitive assignment of Employee, Customer, or Manager to a Person variable and invoking a member on Person actually invokes the member on the instantiated class. This behavior is intuitive. In past version of .NET--prior to 4.0--things got hinky when you tried to assign things like generic delegates with parameters of a narrower type to delegates declared with a narrower type, covariance extends assignment compatibility into these areas. And, of course, if you declared something like a generic delegate with a narrower type, like Employee, and tried to assign it a generic delegate with a wider type it didn't work. Support for this direction of assignment compatibility is contravariance.

Collectively variance--covariance and contravariance--were implemented to extended and preserve or reverse assignment compatibility into generics, collections, and interfaces.

## Understanding Covariance

I mistakenly started this article by limiting the discussion in such a way that I was really talking about assignment compatibility. Fortunately, I wasn't sure and Microsoft alums Charlie Calvert and Eric Lippert set me straight (or straighter). If I still haven't perfected an explanation then the fault is my own. However, with many of the blogs, white papers, and even MSDN help focusing on the "wider to narrower" and "narrower to wider" assignment bits the subject seems a little abstruse.

Rather than go into the math, you can read Eric Lippert's blog for more on that subject, let's continue our example using the Person hierarchy and an exemplar to illustrate the mechanics of support for variance in .NET 4.0. Keep in mind that variance is not limited to generic delegates; it is extended into other things like interfaces. I have simply elected to demonstrate a technical aspect for practical purposes rather than continue an abstract discussion of the subject.

Suppose there is an operation you want to perform on an Employee object and you do it through a Person variable. You would intuitively declare a Person and initialize an instance of Employee.

```Dim p as Person = New Employee()
```

If Customer inherits from Person then this is intuitive. For example, you might want to create an employee report using a collection of employees that validly might contain instances of Employee and Manager objects. Now suppose you wanted to create the Employee/Manager object through a function that determined which kind of object to create based on some logic like a database field. You could use a generic delegate for the construction operation (and we'll use a Lambda Expression for brevity; just keep in mind that Lambda Expressions are just short functions). You might write the following code-see Listing 1:

```Module Module1

Delegate Function MyFunction(Of T)() As T

Sub Main()

' doesn't work pre .NET 4.0 and doesn't work as defined
Dim getEmployee As MyFunction(Of Employee) = Function() New Employee()
Dim getPerson As MyFunction(Of Person) = getEmployee

End Sub
End Module

Class Person

End Class

Class Customer
Inherits Person
End Class

Class Employee
Inherits Person
End Class

Class Manager
Inherits Employee

End Class
```
Listing 1: An Employee is a Person so it would seem that the following code would work as defined; it doesn't.

Suppose for argument sake that `getEmployee` contained logic to determine if a Manager or Employee object was constructed, although clearly it doesn't. Since an Employee (and Manager) descend from Person it seems intuitive that the code in Listing 1 will work. That is that `getPerson` can be assigned `getEmployee`, covariance, because Employee and Manager are narrower types than Person assignment, compatibility should be supported. Prior to .NET framework 4.0 it wasn't (in this scenario) and as defined above it is not.

To make the code compile you need to change the parameter `T in MyFunction` to an Out parameter. The code will compile and work because using the Out modifier makes the generic delegate `MyFunction` covariant-see Listing 2.

```Module Module1

Delegate Function MyFunction(Of Out T)() As T

Sub Main()

' covariance
Dim getEmployee As MyFunction(Of Employee) = Function() New Employee()
Dim getPerson As MyFunction(Of Person) = getEmployee

End Sub

End Module

Class Person

End Class

Class Customer
Inherits Person
End Class

Class Employee
Inherits Person
End Class

Class Manager
Inherits Employee

End Class
```
Listing 2: Changing the generic parameter T to an Out parameter in effect turns on covariance, allowing the assignment of the narrower type parameter Employee to a wider type parameter Person.

With this minor revision assignment compatibility from the narrower (child) type of Employee to the wider (parent) type Person is preserved.

Contravriance in essence reverses the assignment compatibility and extends it, so that a wider type can be assigned to a narrower type.

## Understanding Contravariance

If assignment compatibility is both covariant and contravariant then it is said to be invariant. Covariance as demonstrated, preserves assignment compatibility into areas where it previously was unsupported. Contravariance reverses the direction of compatibility and extends it into areas where prior to .NET framework 4.0 it didn't exist.

Again if you just define a generic delegate in the usual manner and declare an instance of the wider type parameter and then assign that to an instance of the delegate with a narrower type parameter, you would think it would compile. It won't without a change. Listing 3 shows the wrong way to define contravariance, and Listing 4 shows that the In modifier permits the mechanics work.

```Module Module1

Delegate Sub MyAction(Of T)(ByVal parm As T)

Sub Main()

' not quite contravariance
Dim printPerson As MyAction(Of Person) = Sub(person) Console.WriteLine(person)
Dim printEmployee As MyAction(Of Employee) = printPerson

End Sub

End Module

Class Person

End Class

Class Customer
Inherits Person
End Class

Class Employee
Inherits Person
End Class

Class Manager
Inherits Employee

End Class
```
Listing 3: Ooops! Not quite contravariant.

```Module Module1

Delegate Sub MyAction(Of In T)(ByVal parm As T)

Sub Main()

' contravariance
Dim printPerson As MyAction(Of Person) = Sub(person) Console.WriteLine(person)
Dim printEmployee As MyAction(Of Employee) = printPerson
End Sub

End Module

Class Person

End Class

Class Customer
Inherits Person
End Class

Class Employee
Inherits Person
End Class

Class Manager
Inherits Employee

End Class
```
Listing 4: Changing the generic parameter to use the In modifier and contravariance is supported.

By changing the generic parameter to use the In modifier reverse assignment compatibility is supported.

In a general sense think of this as operations on Person may be useful to extend to child types like Employee. (The use of Lambda Expressions is not relevant.) The compiler also does a good job of making sure that the contravariant assignment is permissible. For example, assigning a delegate that performs an operation on Employee to a delegate for a Customer won't compile because Customers are Person (objects) but not Employee objects.

## Summary

Assignment compatibility is the ability to assign variables of one type to instances of another. Covariance preserves assignment compatibility from children to parents (narrower to wider), and contravariance reverses assignment compatibility, supporting assignment of parent types to child types (wider to narrower types).

For the math concepts check out Eric Lippert's blog, and look into variance for interfaces, delegates, and generics for more information on the subject.

## Resources

http://blogs.msdn.com/b/ericlippert/archive/2009/11/30/what-s-the-difference-between-covariance-and-assignment-compatibility.aspx?wa=wsignin1.0

Share:
Home
Mobile Site | Full Site