Understanding Covariant and Contravariant Interfaces in C#

Understanding Covariant and Contravariant interfaces in C#

With <out T>, you can treat the interface reference as one upwards in the hierarchy.

With <in T>, you can treat the interface reference as one downwards in the hiearchy.

Let me try to explain it in more english terms.

Let's say you are retrieving a list of animals from your zoo, and you intend to process them. All animals (in your zoo) have a name, and a unique ID. Some animals are mammals, some are reptiles, some are amphibians, some are fish, etc. but they're all animals.

So, with your list of animals (which contains animals of different types), you can say that all the animals have a name, so obviously it would be safe to get the name of all the animals.

However, what if you have a list of fishes only, but need to treat them like animals, does that work? Intuitively, it should work, but in C# 3.0 and before, this piece of code will not compile:

IEnumerable<Animal> animals = GetFishes(); // returns IEnumerable<Fish>

The reason for this is that the compiler doesn't "know" what you intend, or can, do with the animals collection after you've retrieved it. For all it knows, there could be a way through IEnumerable<T> to put an object back into the list, and that would potentially allow you to put an animal that isn't a fish, into a collection that is supposed to contain only fish.

In other words, the compiler cannot guarantee that this is not allowed:

animals.Add(new Mammal("Zebra"));

So the compiler just outright refuses to compile your code. This is covariance.

Let's look at contravariance.

Since our zoo can handle all animals, it can certainly handle fish, so let's try to add some fish to our zoo.

In C# 3.0 and before, this does not compile:

List<Fish> fishes = GetAccessToFishes(); // for some reason, returns List<Animal>
fishes.Add(new Fish("Guppy"));

Here, the compiler could allow this piece of code, even though the method returns List<Animal> simply because all fishes are animals, so if we just changed the types to this:

List<Animal> fishes = GetAccessToFishes();
fishes.Add(new Fish("Guppy"));

Then it would work, but the compiler cannot determine that you're not trying to do this:

List<Fish> fishes = GetAccessToFishes(); // for some reason, returns List<Animal>
Fish firstFist = fishes[0];

Since the list is actually a list of animals, this is not allowed.

So contra- and co-variance is how you treat object references and what you're allowed to do with them.

The in and out keywords in C# 4.0 specifically marks the interface as one or the other. With in, you're allowed to place the generic type (usually T) in input-positions, which means method arguments, and write-only properties.

With out, you're allowed to place the generic type in output-positions, which is method return values, read-only properties, and out method parameters.

This will allow you to do what intended to do with the code:

IEnumerable<Animal> animals = GetFishes(); // returns IEnumerable<Fish>
// since we can only get animals *out* of the collection, every fish is an animal
// so this is safe

List<T> has both in- and out-directions on T, so it is neither co-variant nor contra-variant, but an interface that allowed you to add objects, like this:

interface IWriteOnlyList<in T>
{
void Add(T value);
}

would allow you to do this:

IWriteOnlyList<Fish> fishes = GetWriteAccessToAnimals(); // still returns
IWriteOnlyList<Animal>
fishes.Add(new Fish("Guppy")); <-- this is now safe

Here's a few videos that shows the concepts:

  • Covariance and Contravariance - VS2010 C# Part 1 of 3
  • Covariance and Contravariance - VS2010 C# Part 2 of 3
  • Covariance and Contravariance - VS2010 C# Part 3 of 3

Here's an example:

namespace SO2719954
{
class Base { }
class Descendant : Base { }

interface IBibbleOut<out T> { }
interface IBibbleIn<in T> { }

class Program
{
static void Main(string[] args)
{
// We can do this since every Descendant is also a Base
// and there is no chance we can put Base objects into
// the returned object, since T is "out"
// We can not, however, put Base objects into b, since all
// Base objects might not be Descendant.
IBibbleOut<Base> b = GetOutDescendant();

// We can do this since every Descendant is also a Base
// and we can now put Descendant objects into Base
// We can not, however, retrieve Descendant objects out
// of d, since all Base objects might not be Descendant
IBibbleIn<Descendant> d = GetInBase();
}

static IBibbleOut<Descendant> GetOutDescendant()
{
return null;
}

static IBibbleIn<Base> GetInBase()
{
return null;
}
}
}

Without these marks, the following could compile:

public List<Descendant> GetDescendants() ...
List<Base> bases = GetDescendants();
bases.Add(new Base()); <-- uh-oh, we try to add a Base to a Descendant

or this:

public List<Base> GetBases() ...
List<Descendant> descendants = GetBases(); <-- uh-oh, we try to treat all Bases
as Descendants

Covariance and contravariance real world example

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());

Covariant interface with contravariant interface as member property

The solution was to remove E as an acceptable argument for instance member methods of IFoobarWriter.

public interface IFoobarWriter<out E>
where E : class, IFoo
{
void Add(IFoo foo);
int Delete(IFoo foo);
object Update(IFoo foo);
}

By having Add, Delete, and Update accept IFoo they effectively limit the types they can work on (as opposed to setting the argument to object) well enough for certain business requirements.

Having the type parameter E for IFoobarWriter remain covariant allows it to remain a part of the IFoobarStore interface.

still confused about covariance and contravariance & in/out

Both covariance and contravariance in C# 4.0 refer to the ability of using a derived class instead of base class. The in/out keywords are compiler hints to indicate whether or not the type parameters will be used for input and output.

Covariance

Covariance in C# 4.0 is aided by out keyword and it means that a generic type using a derived class of the out type parameter is OK. Hence

IEnumerable<Fruit> fruit = new List<Apple>();

Since Apple is a Fruit, List<Apple> can be safely used as IEnumerable<Fruit>

Contravariance

Contravariance is the in keyword and it denotes input types, usually in delegates. The principle is the same, it means that the delegate can accept more derived class.

public delegate void Func<in T>(T param);

This means that if we have a Func<Fruit>, it can be converted to Func<Apple>.

Func<Fruit> fruitFunc = (fruit)=>{};
Func<Apple> appleFunc = fruitFunc;

Why are they called co/contravariance if they are basically the same thing?

Because even though the principle is the same, safe casting from derived to base, when used on the input types, we can safely cast a less derived type (Func<Fruit>) to a more derived type (Func<Apple>), which makes sense, since any function that takes Fruit, can also take Apple.

Contravariance explained

Update: Ooops. As it turned out, I mixed up variance and "assignment compatibility" in my initial answer. Edited the answer accordingly. Also I wrote a blog post that I hope should answer such questions better: Covariance and Contravariance FAQ

Answer: I guess the answer to your first question is that you don't have contravariance in this example:

bool Compare(Mammal mammal1, Mammal mammal2); 
Mammal mammal1 = new Giraffe(); //covariant - no
Mammal mammal2 = new Dolphin(); //covariant - no

Compare(mammal1, mammal2); //covariant or contravariant? - neither
//or
Compare(new Giraffe(), new Dolphin()); //covariant or contravariant? - neither

Furthermore, you don't even have covariance here. What you have is called "assignment compatibility", which means that you can always assign an instance of a more derived type to an instance of a less derived type.

In C#, variance is supported for arrays, delegates, and generic interfaces. As Eric Lippert said in his blog post What's the difference between covariance and assignment compatibility? is that it's better to think about variance as "projection" of types.

Covariance is easier to understand, because it follows the assignment compatibility rules (array of a more derived type can be assigned to an array of a less derived type, "object[] objs = new string[10];"). Contravariance reverses these rules. For example, imagine that you could do something like "string[] strings = new object[10];". Of course, you can't do this because of obvious reasons. But that would be contravariance (but again, arrays are not contravariant, they support covariance only).

Here are the examples from MSDN that I hope will show you what contravariance really means (I own these documents now, so if you think something is unclear in the docs, feel free to give me feedback):

  1. Using Variance in Interfaces for Generic Collections

    Employee[] employees = new Employee[3];
    // You can pass PersonComparer,
    // which implements IEqualityComparer<Person>,
    // although the method expects IEqualityComparer<Employee>.
    IEnumerable<Employee> noduplicates =
    employees.Distinct<Employee>(new PersonComparer());
  2. Using Variance in Delegates

    // Event hander that accepts a parameter of the EventArgs type.
    private void MultiHandler(object sender, System.EventArgs e)
    {
    label1.Text = System.DateTime.Now.ToString();
    }
    public Form1()
    {
    InitializeComponent();
    // You can use a method that has an EventArgs parameter,
    // although the event expects the KeyEventArgs parameter.
    this.button1.KeyDown += this.MultiHandler;
    // You can use the same method
    // for an event that expects the MouseEventArgs parameter.
    this.button1.MouseClick += this.MultiHandler;
    }
  3. Using Variance for Func and Action Generic Delegates

     static void AddToContacts(Person person)
    {
    // This method adds a Person object
    // to a contact list.
    }

    // The Action delegate expects
    // a method that has an Employee parameter,
    // but you can assign it a method that has a Person parameter
    // because Employee derives from Person.
    Action<Employee> addEmployeeToContacts = AddToContacts;

Hope this helps.

Problem understanding covariance contravariance with generics in C#

The error message is insufficiently informative, and that is my fault. Sorry about that.

The problem you are experiencing is a consequence of the fact that covariance only works on reference types.

You're probably saying "but IA is a reference type" right now. Yes, it is. But you didn't say that T is equal to IA. You said that T is a type which implements IA, and a value type can implement an interface. Therefore we do not know whether covariance will work, and we disallow it.

If you want covariance to work you have to tell the compiler that the type parameter is a reference type with the class constraint as well as the IA interface constraint.

The error message really should say that the conversion is not possible because covariance requires a guarantee of reference-type-ness, since that is the fundamental problem.

Interface covariance contravariance : why is this not compiling?


I don't understand why, because IncrementHandler implements Handler<Increment> and Increment implements Command

Let's fix your misunderstanding, and then the rest will become clear.

Suppose what you wanted to do was legal. What goes wrong?

IncrementHandler ih = whatever;
Handler<Command> h = ih; // This is illegal. Suppose it is legal.

now we make a class

public class Decrement : Command { ... }

And now we pass it to h:

Decrement d = new Decrement();
h.Handle(d);

This is legal, because Handler<Command>.Handle takes a Command, and a Decrement is a Command.

So what happened? You just passed a decrement command to ih, via h, but ih is an IncrementHandler that only knows how to handle increments.

Since that is nonsensical, something in here has to be illegal; which line would you like to be illegal? The C# team decided that the conversion is the thing that should be illegal.

More specifically:

Your program is using reflection in an attempted end-run around the type system's safety checks, and then you are complaining that the type system is stopping you when you write something unsafe. Why are you using generics at all?

Generics are (in part) to ensure type safety, and then you are doing a dispatch based on reflection. This doesn't make any sense; don't take steps to increase type safety and then do heroic efforts to work around them.

Plainly you wish to work around type safety, so don't use generics at all. Just make an ICommand interface and a Handler class that takes a command, and then have some mechanism for working out how to dispatch commands.

What I don't understand though is why there are two kinds of things at all. If you want to execute a command, then why not simply put the execution logic on the command object?

There are also other design patterns you could use here other than this clunky dictionary lookup based on types. For example:

  • a command handler could have a method that takes a command and returns a boolean, whether the handler can handle this command or not. Now you have a list of command handlers, a command comes in, and you just run down the list asking "are you my handler?" until you find one. If O(n) lookup is too slow, then build a MRU cache or memoize the result or some such thing, and the amortized behaviour will improve.

  • the dispatch logic could be put into the command handler itself. A command handler is given a command; it either executes it, or it recurses, calling its parent command handler. You can thus build a graph of command handlers that defer work to each other as necessary. (This is basically how QueryService works in COM.)



Related Topics



Leave a reply



Submit