Coco07
Coco07

Reputation: 53

C# dont understand covariance and contravariance of delegates

For a long time now I have been trying to understand the usefulness of the "in" and "out" parameters in connection with generics in C # and I just can't get it into my head(I know how often this question is asked on StackOverflow). I generally understand what covariance and contravariance is, just not why "in" and "out" parameters have to be used.

The following example:

public class MainClass {

delegate TOut MyDelegate<TIn, TOut>(TIn input);

public static void Main()
{
    // Func Delegate is using "in T, out TResult"
    Func<Dog, Mammal> funcDelegate = TestMethod;

    // not using "in" or "out" parameters
    MyDelegate<Dog, Mammal> myDelegate = TestMethod;
}

static Dog TestMethod(Mammal m) { return new Dog(); }

class Mammal { }
class Dog : Mammal { } }//end of class

Why does the Func delegate make use of "in" and "out" when my own delegate without "in" and "out" can also reference a method that is covariant and contravariant?

Upvotes: 5

Views: 921

Answers (2)

Pavel Anikhouski
Pavel Anikhouski

Reputation: 23258

Following the comments under the question, the Variance in Delegates document will explain that

.NET Framework 3.5 introduced variance support for matching method signatures with delegate types in all delegates in C#. This means that you can assign to delegates not only methods that have matching signatures, but also methods that return more derived types (covariance) or that accept parameters that have less derived types (contravariance) than that specified by the delegate type.

So, your assignment MyDelegate<Dog, Mammal> myDelegate = TestMethod; is perfectly fine, despite different signatures on delegate and TestMethod (reversed input parameter and return type).

But in and out parameters are still needed when you have an implicit conversion between delegates, according to Variance in Generic Type Parameters section

To enable implicit conversion, you must explicitly declare generic parameters in a delegate as covariant or contravariant by using the in or out keyword.

E.g. the following code will not compile

MyDelegate<Dog, Mammal> myDelegate = TestMethod;
MyDelegate<Dog, Dog> anotherDelegate = TestMethod;
myDelegate = anotherDelegate; //error CS0029: Cannot implicitly convert type...

Until you declare MyDelegate witn contravariant parameter and covariant return type

delegate TOut MyDelegate<in TIn, out TOut>(TIn input);

After that last line will compile

Upvotes: 3

Guru Stron
Guru Stron

Reputation: 142903

In addition to @Pavel Anikhouski comment (linking this article), variance will come into play when you will try to do next, for example:

Func<Mammal, Mammal> someFunc = TestMethod;
Func<Dog, Mammal> funcDelegate = someFunc;
MyDelegate<Mammal, Mammal> someDelegate = TestMethod;
MyDelegate<Dog, Mammal> myDelegate = someDelegate; // will not compile unless you will declare MyDelegate<in TIn, TOut>

Upvotes: 2

Related Questions