7

I'm having a struggle understanding these two concepts. But I think after many videos and SO QA's, I have it distilled down to its simplest form:

Covariant - Assumes a sub-type can do what its base-type does.
Contravariant - Assumes you can treat a sub-type the same way you would treat its base-type.

Supposing these three classes:

class Animal
{
    void Live(Animal animal)
    {
        //born!
    }

    void Die(Animal animal)
    {
        //dead!
    }

}

class Cat : Animal
{

}

class Dog : Animal
{

}

Covariant

Any animal can do what animals do.
Assumes a sub-type can do what its base-type does.

Animal anAnimal = new Cat();
anAnimal.Live();
anAnimal.Die();

Animal anotherAnimal = new Dog();
anotherAnimal.Live();
anotherAnimal.Die();

Contravariant

Anything you can do to an animal, you can do to any animal.
Assumes you can treat a sub-type the same way you would treat its base-type.

Action<Animal> kill = KillTheAnimal;

Cat aCat = new Cat();
KillTheCat(kill, aCat);

Dog = new Dog();
KillTheDog(kill, aDog);

KillTheCat(Action<Cat> action, Cat aCat)
{  
    action(aCat);  
}

KillTheDog(Action<Dog> action, Dog aDog)
{  
    action(aDog);  
}

void KillTheAnimal(Animal anAnimal)
{
    anAnimal.Die();
}

Is this correct? It seems like at the end of the day, what covariance and contravariance allow you to do is simply use behavior you would naturally expect, i.e. every type of animal has all animal characteristics, or more generally - all sub-types implement all features of their base-type. Seems like it's just allowing for the obvious - they just support different mechanisms that allow you to get at that inherited behavior in different ways - one converts from sub-type to base-type (Covariance) and the other converts from base-type to sub-type (Contravariance), but at its very core, both are just allowing behavior of the base class to be invoked.

For example in the cases above, you were just allowing for the fact that the Cat and the Dog sub-types of Animal both have the methods Live and Die - which they very naturally inherited from their base class Animal.

In both cases - covariance and contravariance - we are allowing for invocation of general behavior that is guaranteed because we have made sure that the target the behavior is being invoked on inherits from a specific base class.

In the case of Covariance, we are implicitly casting a sub-type to its base-type and calling the base-type behavior (doesn't matter if the base-type behavior is overridden by the sub-type...the point is, we know it exists).

In the case of Contravariance, we are taking a sub-type and passing it to a function we know only invokes base-type behavior (because the base-type is the formal parameter type), so we are safe to cast the base-type to a sub-type.

richard
  • 12,263
  • 23
  • 95
  • 151
  • Is anybody out there? – richard Oct 10 '13 at 15:58
  • Does nobody know, or is this just a boring question? – richard Oct 13 '13 at 22:56
  • Your definitions, as well as "all sub-types implement all features of their base-type" seem to me more about Liskow Substitution Principle (LSP) than co/contra-variance see http://en.wikipedia.org/wiki/Liskov_substitution_principle I have always just seen covariance (resp. contravariance ) as using a type which is more specific (resp. general) than expected. Some usages are type safe. Some are not, depending on the context (assignment, inheritance overriding) See http://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29 and the considerations on type safety – jbl Oct 15 '13 at 15:19
  • @RichardDesLonde: The bounty was necessary because people are sick of answering this question. Someone asks a variation on it every day. – Brian Oct 16 '13 at 14:28
  • @Brian, yes but its such an important and somewhat confusing topic, so all the variations on answers (as long as they are correct) are really necessary. I often read several answers to the same question, all explained differently, and it's only on the 3rd or 4th that someone will explain it in a way that *I* can understand. – richard Oct 16 '13 at 15:35
  • possible duplicate of [C# : Is Variance (Covariance / Contravariance) another word for Polymorphism?](http://stackoverflow.com/questions/1078423/c-sharp-is-variance-covariance-contravariance-another-word-for-polymorphis) – nawfal Jul 07 '14 at 09:32

3 Answers3

8

Variance - refers to how complex types (arrays, lists, delegates, generics) relate to the direction of subtyping of their underlying types.

In other words it is about in what direction is allowed to implicitly cast complex types.

Example of the relation of two complex types (delegates) according to their underlying types Animal and Cat.

Covariance is a preserved direction of implicit casting as to subtyping direction (Animal<-Cat)

// Covariance based on type of return param of delegate
var catDelegate = new Func<Cat>(delegate {return null;});

// Allowed implicit casting from delegate based on Cat return param 
// to delegate based on Animal return param 
Func<Animal> animalDelegate = catDelegate;

Contravariance is a reversed direction of implicit casting as to subtyping direction (Animal->Cat)

// contravariance based on type of passed arguments of delegate
var animalDelegate = new Action<Animal>(delegate{});

// Allowed implicit casting from delegate based on Animal passed param 
// to delegate based on Cat passed param
Action<Cat> catDelegate = animalDelegate;

Invariance is a unsupported implicit casting (in any direction)

Generic lists are invariant

List<Animal> animals = new List<Cat>(); // error!
List<Cat> animals = new List<Animal>(); // error!

Examples of supported variance in C#

Arrays are covariant

Animal[] animals = new Cat[10]; // possible

Generic IEnumerable is covariant

IEnumerable<Animal> animals = new List<Cat>(); // possible
Gennady
  • 216
  • 1
  • 3
  • 1
    Note that `Animal[] animals = new Cat[10];` [is unsafe](http://blogs.msdn.com/b/ericlippert/archive/2007/10/17/covariance-and-contravariance-in-c-part-two-array-covariance.aspx). – Brian Oct 16 '13 at 14:26
7

I'm having a struggle understanding these two concepts.

Yes you are. Many people do.

But I think after many videos and SO QA's, I have it distilled down to its simplest form:

You have not.

Covariance means that a sub-type can do what its base-type does.

No. That's the Liskov Substitution Principle.

Contravariance means you can treat a sub-type the same way you would treat its base-type.

No. That's just re-stating what you said for covariance.

The real distillation of covariance and contravariance is:

  • A covariant conversion preserves the direction of another conversion.

  • A contravariant conversion reverses the direction of another conversion.

Dog is convertible to Animal. IEnumerable<Dog> is convertible to IEnumerable<Animal>. The direction is preserved, so IEnumerable<T> is covariant. IComparable<Animal> is convertible to IComparable<Dog>, which reverses the direction of the conversion, so it is contravariant.

I understand mathematically what covariance means, and so I guess it's the same in compsci.

Just to be clear: mathematicians use "variance" to mean a bunch of different things. The meaning that is common to mathematics and computer science is the category theory definition.

In C# it's just a matter of where and in what ways these two types of relationships are supported?

Mathematically, variance tells you about whether a relation is preserved or reversed by a mapping. If we have the mapping T --> IEnumerable<T> and the relation "is convertible to via identity or reference conversion" then it is the case that in C#, if X relates to Y then IE<X> relates to IE<Y>. The mapping is therefore said to be covariant with respect to the relation.

what is it that these features are trying to accomplish by supporting them?

People frequently requested "I have a method that takes a sequence of animals and I have a sequence of turtles in hand; why do I have to copy the sequence to a new sequence to use the method?" That's a reasonable request, we got it frequently, and we got it a lot more frequently after LINQ made it easier to work with sequences. It's a generally useful feature that we could implement at a reasonable cost, so we implemented it.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • Eric, thank you. I understand mathematically what covariance means, and so I guess it's the same in compsci. In C# it's just a matter of where and in what ways these two types of relationships are supported? – richard Oct 16 '13 at 15:37
  • I guess my struggle here is that in the examples given (and I ripped them shamelessly from one of your other answers), at the heart we are only trying to get at base class behavior (`Animal.Live` and `Animal.Die`) - and we use these two principles and the ways they are supported in C# to get at them. Does that sound correct? – richard Oct 16 '13 at 15:40
  • So I should just think of these as the concepts you described (preserving and reversing relationships) and then see the various ways they are supported in C#...but then tell me why they are supported in C#...what is it that these features are trying to accomplish by supporting them? – richard Oct 16 '13 at 15:53
  • @RichardDesLonde: I've addressed your follow-up questions. – Eric Lippert Oct 16 '13 at 16:17
  • thx for this explanation, as well as for the blog post linked in Praggie's answer – jbl Oct 16 '13 at 19:27
  • Eric thanks so much. I really appreciate you helping me understand this question especially since I know you have blogged and written about it extensively before. :-) – richard Oct 16 '13 at 19:57
2

I think we are limiting the scope of covariance and contravariance if we are just thinking in terms of base type and sub type and how base type behavior is called. Real merit of contravarinace and covariance comes in terms of what kind of types (projections as explained by Eric lippert http://blogs.msdn.com/b/ericlippert/archive/2009/11/30/what-s-the-difference-between-covariance-and-assignment-compatibility.aspx) can be created using them. Following faqs on variance should be able to clear your doubt. http://blogs.msdn.com/b/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx

Praggie
  • 396
  • 2
  • 12