172

I'm having a little trouble understanding how I would use covariance and contravariance in the real world.

So far, the only examples I've seen have been the same old array example.

object[] objectArray = new string[] { "string 1", "string 2" };

It would be nice to see an example that would allow me to use it during my development if I could see it being used elsewhere.

a14m
  • 6,653
  • 7
  • 49
  • 62
Razor
  • 16,811
  • 22
  • 87
  • 137
  • 2
    I explore *covariance* in this [answer](http://stackoverflow.com/a/17231728/11545) to (my own) question: [covariance types: by example](http://stackoverflow.com/q/17231577/11545). I think you'll find it interesting, and hopefully instructive. – Cristian Diaconescu Jun 28 '13 at 11:15

9 Answers9

143
// Contravariance
interface IGobbler<in T> {
    void gobble(T t);
}

// Since a QuadrupedGobbler can gobble any four-footed
// creature, it is OK to treat it as a donkey gobbler.
IGobbler<Donkey> dg = new QuadrupedGobbler();
dg.gobble(MyDonkey());

// Covariance
interface ISpewer<out T> {
    T spew();
}

// A MouseSpewer obviously spews rodents (all mice are
// rodents), so we can treat it as a rodent spewer.
ISpewer<Rodent> rs = new MouseSpewer();
Rodent r = rs.spew();

For completeness…

// Invariance
interface IHat<T> {
    void hide(T t);
    T pull();
}

// A RabbitHat…
IHat<Rabbit> rHat = RabbitHat();

// …cannot be treated covariantly as a mammal hat…
IHat<Mammal> mHat = rHat;      // Compiler error
// …because…
mHat.hide(new Dolphin());      // Hide a dolphin in a rabbit hat??

// It also cannot be treated contravariantly as a cottontail hat…
IHat<CottonTail> cHat = rHat;  // Compiler error
// …because…
rHat.hide(new MarshRabbit());
cHat.pull();                   // Pull a marsh rabbit out of a cottontail hat??
Marcelo Cantos
  • 167,268
  • 37
  • 309
  • 353
  • 149
    I like this realistic example. I was just writing some donkey gobbling code last week and i was so glad that we have covariance now. :-) – Eric Lippert Apr 18 '10 at 15:26
  • 4
    This comment above with @javadba telling THE EricLippert what is covariance and contravariance is a realistic covariant example of me telling my granny how to suck eggs! :p – iAteABug_And_iLiked_it Dec 30 '14 at 13:47
  • 1
    The question didn't ask what contravariance and covariance *can do*, it asked *why would you need to use it*. Your example is far from practical because it doesn't require either. I can create a QuadrupedGobbler and treat it as itself (assign it to IGobbler) and it can still gobble up Donkeys (I can pass a Donkey in to the Gobble method that requires a Quadruped). No contravariance needed. That's cool that we *can* treat a QuadrupedGobbler as a DonkeyGobbler, but why would we need to, in this case, if a QuadrupedGobbler can already gobble up Donkeys? – wired_in Feb 27 '16 at 00:21
  • 1
    @wired_in Because when you only care about donkeys, being more general can get in the way. For example, if you have a farm that supplies donkeys to be gobbled, you can express this as `void feed(IGobbler dg)`. If you took an IGobbler as a parameter instead, you couldn't pass in a dragon that only eats donkeys. – Marcelo Cantos Feb 27 '16 at 00:41
  • You don't need to explain it to me, it's not my question. The point is that should have been your example in the first place, instead of the above answer. – wired_in Mar 03 '16 at 02:10
  • I am sorry for dump question, what is MyDonkey() here ? – shanmugharaj Nov 01 '16 at 03:31
  • @shansfk a hypothetical function that returns a Donkey. – Marcelo Cantos Nov 01 '16 at 08:41
  • @wired_in nonsense, the question asks for examples of *how* it would be used, not why. And the above answer answers that very well. The why also becomes fairly clear from the donkey and mouse examples anyway. – Stephen Holt May 22 '18 at 08:28
  • @StephenHolt Verbatim, the question asks "how I would use covariance and contravariance in the real world." Clearly that means he wants a practical example in which it makes sense to use contravariance and covariance. So no, it is not nonsense to point out that the example above is not practical, because neither covariance nor contravariance is needed to achieve the desired result, so it would only serve to over-complicate something simple. – wired_in May 24 '18 at 18:41
  • 1
    Waaay late to the party, but this is about the best written example I've seen around SO. Makes complete sense while being ridiculous. I'm going to have to up my game with answers... – Jesse Williams Nov 16 '18 at 14:24
  • I can never remember which way round co/contravariance is, I will now. I don't know why I found this so funny! – Reznoir Sep 20 '19 at 15:32
138

Here's what I put together to help me understand the difference

public interface ICovariant<out T> { }
public interface IContravariant<in T> { }

public class Covariant<T> : ICovariant<T> { }
public class Contravariant<T> : IContravariant<T> { }

public class Fruit { }
public class Apple : Fruit { }

public class TheInsAndOuts
{
    public void Covariance()
    {
        ICovariant<Fruit> fruit = new Covariant<Fruit>();
        ICovariant<Apple> apple = new Covariant<Apple>();

        Covariant(fruit);
        Covariant(apple); //apple is being upcasted to fruit, without the out keyword this will not compile
    }

    public void Contravariance()
    {
        IContravariant<Fruit> fruit = new Contravariant<Fruit>();
        IContravariant<Apple> apple = new Contravariant<Apple>();

        Contravariant(fruit); //fruit is being downcasted to apple, without the in keyword this will not compile
        Contravariant(apple);
    }

    public void Covariant(ICovariant<Fruit> fruit) { }

    public void Contravariant(IContravariant<Apple> apple) { }
}

tldr

ICovariant<Fruit> apple = new Covariant<Apple>(); //because it's covariant
IContravariant<Apple> fruit = new Contravariant<Fruit>(); //because it's contravariant
CSharper
  • 4,984
  • 5
  • 23
  • 48
  • 11
    This is the best thing I have seen so far that is clear and concise.Great example! – Rob L Aug 21 '16 at 17:14
  • 6
    How can the fruit be downcasted to apple (in the `Contravariance` example) when `Fruit` is the parent of `Apple`? – Tobias Marschall Aug 19 '18 at 07:26
  • @TobiasMarschall that means you have to study more over "polymorphism" – snr Mar 23 '20 at 06:52
  • I feel the confusion is more about "why". The fruit is downcasted to apple, and passed into some function. But the function will still use the potentially fake apple as the original fruit instead of an apple. I feel the contravarince is just for convenience that allows a less derived type to be passed in as a more derived type, but it will still be used as the original less derived type. – DavidY Feb 20 '21 at 13:52
117

Let's say you have a class Person and a class that derives from it, Teacher. You have some operations that take an IEnumerable<Person> as the argument. In your School class you have a method that returns an IEnumerable<Teacher>. Covariance allows you to directly use that result for the methods that take an IEnumerable<Person>, substituting a more derived type for a less derived (more generic) type. Contravariance, counter-intuitively, allows you to use a more generic type, where a more derived type is specified.

See also Covariance and Contravariance in Generics on MSDN.

Classes:

public class Person 
{
     public string Name { get; set; }
} 

public class Teacher : Person { } 

public class MailingList
{
    public void Add(IEnumerable<out Person> people) { ... }
}

public class School
{
    public IEnumerable<Teacher> GetTeachers() { ... }
}

public class PersonNameComparer : IComparer<Person>
{
    public int Compare(Person a, Person b) 
    { 
        if (a == null) return b == null ? 0 : -1;
        return b == null ? 1 : Compare(a,b);
    }

    private int Compare(string a, string b)
    {
        if (a == null) return b == null ? 0 : -1;
        return b == null ? 1 : a.CompareTo(b);
    }
}

Usage:

var teachers = school.GetTeachers();
var mailingList = new MailingList();

// Add() is covariant, we can use a more derived type
mailingList.Add(teachers);

// the Set<T> constructor uses a contravariant interface, IComparer<in T>,
// we can use a more generic type than required.
// See https://msdn.microsoft.com/en-us/library/8ehhxeaf.aspx for declaration syntax
var teacherSet = new SortedSet<Teachers>(teachers, new PersonNameComparer());
snr
  • 13,515
  • 2
  • 48
  • 77
tvanfosson
  • 490,224
  • 93
  • 683
  • 780
  • 15
    @FilipBartuzi - if, like me when I wrote this answer, you were employed at a University that is very much a real world example. – tvanfosson Dec 05 '14 at 02:16
  • 7
    How can this be marked the answer when it doesnt answer the question and doesnt give any example of using co /contra variance in c#? – barakcaf Jun 28 '16 at 12:02
  • @barakcaf added an example of contravariance. not sure why you weren't seeing the example of covariance - perhaps you needed to scroll the code down - but I added some comments around that. – tvanfosson Jun 28 '16 at 13:53
  • @tvanfosson the code uses co/contra, i ment that it doesn't show how to declare it. The example doesn't show usage of in/out in the generic declaration while the other answer does. – barakcaf Jun 28 '16 at 13:58
  • So, if I get it right, covariance is what allows Liskov's substitution principle in C#, is that right? – Miguel Veloso Dec 03 '17 at 10:16
  • @MiguelVeloso covariance doesn't guarantee that Liskov's substitution principle holds, it simply means that a more derived type will satisfy the type constraints, not that the behavior of the derived class isn't "breaking" with respect to the parent, see https://www.infragistics.com/community/blogs/b/dhananjay_kumar/posts/simplifying-the-liskov-substitution-principle-of-solid-in-c for an example. All of the inheritance examples would satisfy the covariant requirements, but not the Liskov Substitution Principle. – tvanfosson Dec 04 '17 at 03:25
  • Thanks @tvanfosson, I'm clear covariance alone could not possibly guarantee Liskov's, as the code in the sub class could do whatever, what I meant was in the sense of (the other way around): Could Liskov's substitution principle hold if covariance did not exist? – Miguel Veloso Dec 04 '17 at 19:02
  • This doesn't compile. The method MailingList.Add gives error 'Invalid variance modifier. Only interface and delegate types can be specified as variant'. The reason is, you don't specify covariance at 'use-time' like that. The IEnumerable interface is already covariant - it's declared as IEnumerable - and so you can use it covariantly. Remove the out modifier and the code works. – Tor Haugen Mar 20 '20 at 13:57
60

Here's a simple example using an inheritance hierarchy.

Given the simple class hierarchy:

enter image description here

And in code:

public abstract class LifeForm  { }
public abstract class Animal : LifeForm { }
public class Giraffe : Animal { }
public class Zebra : Animal { }

Invariance (i.e. generic type parameters not decorated with in or out keywords)

Seemingly, a method such as this

public static void PrintLifeForms(IList<LifeForm> lifeForms)
{
    foreach (var lifeForm in lifeForms)
    {
        Console.WriteLine(lifeForm.GetType().ToString());
    }
}

... should accept a heterogeneous collection: (which it does)

var myAnimals = new List<LifeForm>
{
    new Giraffe(),
    new Zebra()
};
PrintLifeForms(myAnimals); // Giraffe, Zebra

However, passing a collection of a more derived type fails!

var myGiraffes = new List<Giraffe>
{
    new Giraffe(), // "Jerry"
    new Giraffe() // "Melman"
};
PrintLifeForms(myGiraffes); // Compile Error!

cannot convert from 'System.Collections.Generic.List<Giraffe>' to 'System.Collections.Generic.IList<LifeForm>'

Why? Because the generic parameter IList<LifeForm> is not covariant - IList<T> is invariant, so IList<LifeForm> only accepts collections (which implement IList) where the parameterized type T must be LifeForm.

If the method implementation of PrintLifeForms was malicious (but has same method signature), the reason why the compiler prevents passing List<Giraffe> becomes obvious:

 public static void PrintLifeForms(IList<LifeForm> lifeForms)
 {
     lifeForms.Add(new Zebra());
 }

Since IList permits adding or removal of elements, any subclass of LifeForm could thus be added to the parameter lifeForms, and would violate the type of any collection of derived types passed to the method. (Here, the malicious method would attempt to add a Zebra to var myGiraffes). Fortunately, the compiler protects us from this danger.

Covariance (Generic with parameterized type decorated with out)

Covariance is widely used with immutable collections (i.e. where new elements cannot be added or removed from a collection)

The solution to the example above is to ensure that a covariant generic collection type is used, e.g. IEnumerable (defined as IEnumerable<out T>). IEnumerable has no methods to change to the collection, and as a result of the out covariance, any collection with subtype of LifeForm may now be passed to the method:

public static void PrintLifeForms(IEnumerable<LifeForm> lifeForms)
{
    foreach (var lifeForm in lifeForms)
    {
        Console.WriteLine(lifeForm.GetType().ToString());
    }
}

PrintLifeForms can now be called with Zebras, Giraffes and any IEnumerable<> of any subclass of LifeForm.

var myGiraffes = new List<Giraffe>
{
    new Giraffe(), // "Jerry"
    new Giraffe() // "Melman"
};
PrintLifeForms(myGiraffes); // All good!

Contravariance (Generic with parameterized type decorated with in)

Contravariance is frequently used when functions are passed as parameters.

Here's an example of a function, which takes an Action<Zebra> as a parameter, and invokes it on a known instance of a Zebra:

public void PerformZebraAction(Action<Zebra> zebraAction)
{
    var zebra = new Zebra();
    zebraAction(zebra);
}

As expected, this works just fine:

var myAction = new Action<Zebra>(z => Console.WriteLine("I'm a zebra"));
PerformZebraAction(myAction); // I'm a zebra

Intuitively, this will fail:

var myAction = new Action<Giraffe>(g => Console.WriteLine("I'm a giraffe"));
PerformZebraAction(myAction); 

cannot convert from 'System.Action<Giraffe>' to 'System.Action<Zebra>'

However, this succeeds

var myAction = new Action<Animal>(a => Console.WriteLine("I'm an animal"));
PerformZebraAction(myAction); // I'm an animal

and even this also succeeds:

var myAction = new Action<object>(a => Console.WriteLine("I'm an amoeba"));
PerformZebraAction(myAction); // I'm an amoeba

Why? Because Action is defined as Action<in T>, i.e. it is contravariant, meaning that for Action<Zebra> myAction, that myAction can be at "most" a Action<Zebra>, but less derived superclasses of Zebra are also acceptable.

Although this may be non-intuitive at first (e.g. how can an Action<object> be passed as a parameter requiring Action<Zebra> ?), if you unpack the steps, you will note that the called function (PerformZebraAction) itself is responsible for passing data (in this case a Zebra instance) to the function - the data doesn't come from the calling code.

Because of the inverted approach of using higher order functions in this manner, by the time the Action is invoked, it is the more derived Zebra instance which is invoked against the zebraAction function (passed as a parameter), although the function itself uses a less derived type.

StuartLC
  • 96,413
  • 17
  • 181
  • 256
  • 7
    This is a great explanation for the different variance options, since it talks through the example and also clarifies why the compiler restricts or permits without the in/out keywords – Vikhram Nov 13 '18 at 14:45
  • Where is the `in` keyword used for the _contravariance_ ? – StephenBoesch May 04 '20 at 04:47
  • @javadba in the above, `Action` and `Func` are contravariant in the input type. (My examples use existing invariant (List), covariant (IEnumerable) and contravariant (Action, Func) types) – StuartLC May 04 '20 at 09:53
  • Ok I don't do `C#` so would not know that. – StephenBoesch May 04 '20 at 11:25
  • It's fairly similar in Scala, just different syntax - [+T] would be covariant in T, [-T] would be contravariant in T, Scala can also enforce the 'between' constraint, and the 'Nothing' promiscuous subclass, which C# doesn't have. – StuartLC May 04 '20 at 11:50
57

The in and out keywords control the compiler's casting rules for interfaces and delegates with generic parameters:

interface IInvariant<T> {
    // This interface can not be implicitly cast AT ALL
    // Used for non-readonly collections
    IList<T> GetList { get; }
    // Used when T is used as both argument *and* return type
    T Method(T argument);
}//interface

interface ICovariant<out T> {
    // This interface can be implicitly cast to LESS DERIVED (upcasting)
    // Used for readonly collections
    IEnumerable<T> GetList { get; }
    // Used when T is used as return type
    T Method();
}//interface

interface IContravariant<in T> {
    // This interface can be implicitly cast to MORE DERIVED (downcasting)
    // Usually means T is used as argument
    void Method(T argument);
}//interface

class Casting {

    IInvariant<Animal> invariantAnimal;
    ICovariant<Animal> covariantAnimal;
    IContravariant<Animal> contravariantAnimal;

    IInvariant<Fish> invariantFish;
    ICovariant<Fish> covariantFish;
    IContravariant<Fish> contravariantFish;

    public void Go() {

        // NOT ALLOWED invariants do *not* allow implicit casting:
        invariantAnimal = invariantFish; 
        invariantFish = invariantAnimal; // NOT ALLOWED

        // ALLOWED covariants *allow* implicit upcasting:
        covariantAnimal = covariantFish; 
        // NOT ALLOWED covariants do *not* allow implicit downcasting:
        covariantFish = covariantAnimal; 

        // NOT ALLOWED contravariants do *not* allow implicit upcasting:
        contravariantAnimal = contravariantFish; 
        // ALLOWED contravariants *allow* implicit downcasting
        contravariantFish = contravariantAnimal; 

    }//method

}//class

// .NET Framework Examples:
public interface IList<T> : ICollection<T>, IEnumerable<T>, IEnumerable { }
public interface IEnumerable<out T> : IEnumerable { }


class Delegates {

    // When T is used as both "in" (argument) and "out" (return value)
    delegate T Invariant<T>(T argument);

    // When T is used as "out" (return value) only
    delegate T Covariant<out T>();

    // When T is used as "in" (argument) only
    delegate void Contravariant<in T>(T argument);

    // Confusing
    delegate T CovariantBoth<out T>(T argument);

    // Confusing
    delegate T ContravariantBoth<in T>(T argument);

    // From .NET Framework:
    public delegate void Action<in T>(T obj);
    public delegate TResult Func<in T, out TResult>(T arg);

}//class
Kjartan
  • 17,127
  • 14
  • 67
  • 84
Jack
  • 4,076
  • 2
  • 26
  • 22
32
class A {}
class B : A {}

public void SomeFunction()
{
    var someListOfB = new List<B>();
    someListOfB.Add(new B());
    someListOfB.Add(new B());
    someListOfB.Add(new B());
    SomeFunctionThatTakesA(someListOfB);
}

public void SomeFunctionThatTakesA(IEnumerable<A> input)
{
    // Before C# 4, you couldn't pass in List<B>:
    // cannot convert from
    // 'System.Collections.Generic.List<ConsoleApplication1.B>' to
    // 'System.Collections.Generic.IEnumerable<ConsoleApplication1.A>'
}

Basically whenever you had a function that takes an Enumerable of one type, you couldn't pass in an Enumerable of a derived type without explicitly casting it.

Just to warn you about a trap though:

var ListOfB = new List<B>();
if(ListOfB is IEnumerable<A>)
{
    // In C# 4, this branch will
    // execute...
    Console.Write("It is A");
}
else if (ListOfB is IEnumerable<B>)
{
    // ...but in C# 3 and earlier,
    // this one will execute instead.
    Console.Write("It is B");
}

That is horrible code anyway, but it does exist and the changing behavior in C# 4 might introduce subtle and hard to find bugs if you use a construct like this.

Michael Stum
  • 167,397
  • 108
  • 388
  • 523
  • So this affects collections more than anything, because in c# 3 you could pass a more derived type into a method of a less derived type. – Razor Apr 18 '10 at 13:50
  • 3
    Yes, the big change is that IEnumerable now supports this, whereas it didn't before. – Michael Stum Apr 18 '10 at 13:51
8

Contravariance

In the real world, you can always use a shelter for animals instead of a shelter for rabbits because every time an animal shelter hosts a rabbit it is an animal. However, if you use a rabbit shelter instead of an animal shelter its staff can get eaten by a tiger.

In code, this means that if you have an IShelter<Animal> animals you can simply write IShelter<Rabbit> rabbits = animals if you promise and use T in the IShelter<T> only as method parameters like so:

public class Contravariance
{
    public class Animal { }
    public class Rabbit : Animal { }

    public interface IShelter<in T>
    {
        void Host(T thing);
    }

    public void NoCompileErrors()
    {
        IShelter<Animal> animals = null;
        IShelter<Rabbit> rabbits = null;

        rabbits = animals;
    }
}

and replace an item with a more generic one, i.e. reduce the variance or introduce contravariance.

Covariance

In the real world, you can always use a supplier of rabbits instead of a supplier of animals because every time a rabbit supplier gives you a rabbit it is an animal. However, if you use an animal supplier instead of a rabbit supplier you can get eaten by a tiger.

In code, this means that if you have an ISupply<Rabbit> rabbits you can simply write ISupply<Animal> animals = rabbits if you promise and use T in the ISupply<T> only as method return values like so:

public class Covariance
{
    public class Animal { }
    public class Rabbit : Animal { }

    public interface ISupply<out T>
    {
        T Get();
    }

    public void NoCompileErrors()
    {
        ISupply<Animal> animals = null;
        ISupply<Rabbit> rabbits = null;

        animals = rabbits;
    }
}

and replace an item with a more derived one, i.e. increase the variance or introduce covariance.

All in all, this is just a compile-time checkable promise from you that you would treat a generic type in a certain fashion to keep the type safety and not get anyone eaten.

You might want to give this a read to double-wrap your head around this.

Adrian Mole
  • 30,672
  • 69
  • 32
  • 52
Ivan Rybalko
  • 131
  • 2
  • 4
  • ***you can get eaten by a tiger*** That was worth an upvote – StephenBoesch May 04 '20 at 04:33
  • Your comment on `contravariance` is interesting. I am reading into it as indicating a _operational_ requirement: that the more general type must support the use cases of all types derived from it. So in this case the _animal shelter_ must be able to support sheltering every animal type. In that case adding a new subclass might break the superclass! That is - if we add a subtype _Tyrannosaurus Rex_ then it could wreck our existing _animal shelter_. – StephenBoesch May 04 '20 at 04:39
  • (Continued). That differs sharply from the _covariance_ that is clearly described _structurally_: all more specific sub-types support the operations defined in the super type - but not necessarily in the same manner. – StephenBoesch May 04 '20 at 04:42
5

From MSDN

The following code example shows covariance and contravariance support for method groups

static object GetObject() { return null; }
static void SetObject(object obj) { }

static string GetString() { return ""; }
static void SetString(string str) { }

static void Test()
{
    // Covariance. A delegate specifies a return type as object, 
    // but you can assign a method that returns a string.
    Func<object> del = GetString;

    // Contravariance. A delegate specifies a parameter type as string, 
    // but you can assign a method that takes an object.
    Action<string> del2 = SetObject;
}
Kamran Bigdely
  • 6,715
  • 13
  • 57
  • 82
3

The converter delegate helps me to visualise both concepts working together:

delegate TOutput Converter<in TInput, out TOutput>(TInput input);

TOutput represents covariance where a method returns a more specific type.

TInput represents contravariance where a method is passed a less specific type.

public class Dog { public string Name { get; set; } }
public class Poodle : Dog { public void DoBackflip(){ System.Console.WriteLine("2nd smartest breed - woof!"); } }

public static Poodle ConvertDogToPoodle(Dog dog)
{
    return new Poodle() { Name = dog.Name };
}

List<Dog> dogs = new List<Dog>() { new Dog { Name = "Truffles" }, new Dog { Name = "Fuzzball" } };
List<Poodle> poodles = dogs.ConvertAll(new Converter<Dog, Poodle>(ConvertDogToPoodle));
poodles[0].DoBackflip();
woggles
  • 7,054
  • 11
  • 65
  • 124