3
votes

From Wikipedia:

covariant: converting from wider (double) to narrower (float).
contravariant: converting from narrower (float) to wider (double).

In .NET, a delegate has covariance because it allows for derived types of the delegate's specified return type to be the return type of a method that it holds a reference to.

As well a delegate has contravariance because it allows for derived types of the delegate's specified arguments (parameters) to be the argument type passed into the method that it holds a reference to.

With these two definitions as they pertain to delegates, shouldn't they both be covariance? In both cases, the delegate expects a "wider" type, but is given a "narrower type".

See here for an example of both from MSDN.

So how does the word contravariance make sense, linguistically, when pertaining to delegates?

2

2 Answers

3
votes

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

Last paragraph has a concise summary of assignment compatibility.

//Linguistically, this seems to be logical in the sense of parameters versus return- so the direction //forward or backward is with regards to going in or coming out of the function.

2
votes

I now disagree with the answer from SeanVDH. He says "Linguistically, this seems to be logical in the sense of parameters versus return- so the direction forward or backward is with regards to going in or coming out of the function".

Instead I think this is the answer which comes from here:

Covariance preserves assignment compatibility and contravariance reverses it. Covariance is a widening conversion, and contravariance is a narrowing conversion.

When you instantiate a delegate, you can assign it a method that has a more derived return type than that specified in the delegate (covariance). You can also assign a method that has parameter types less derived than those in the delegate (contravariance). Emphasis added

Example:

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

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

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

    // Contravariance. A delegate specifies a parameter type as string,
    // but I can assign a method that takes an object.
    Action<string> del2 = SetObject;

    // But implicit conversion between generic delegates is not supported until C# 4.0.
    Func<string> del3 = GetString;
    Func<object> del4 = del3; // Compiler error here until C# 4.0.
}

This realization was the result of reading Eric Lippert's article, the Covariance and contravariance section of the Fast-tracked delegates chapter in Jon Skeet's book C# In Depth, as well as the link above where the quote was drawn from.