Reputation: 53
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
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
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