I have the following code, taken from this MSDN:
public class First { }
public class Second : First { }
public delegate First SampleDelegate(Second a);
// Matching signature.
public static First ASecondRFirst(Second first)
{ return new First(); }
// The return type is more derived.
public static Second ASecondRSecond(Second second)
{ return new Second(); }
// The argument type is less derived.
public static First AFirstRFirst(First first)
{ return new First(); }
// The return type is more derived
// and the argument type is less derived.
public static Second AFirstRSecond(First first)
{ return new Second(); }
SampleDelegate test;
test = ASecondRFirst;
test = ASecondRSecond;
test = AFirstRFirst;
test = AFirstRSecond;
This all compiles fine but I wanted to test assigning the delegate to an anonymous lambda expression:
test = (First x) => { return new Second(); };
However on that line I get the error:
Cannot convert lambda expression to type 'SampleDelegate' because the parameter types do not match the delegate parameter types
Namely: Parameter 1 is declared as type 'ConsoleApp1.First' but should be ConsoleApp1.Second' ("ConsoleApp1" is the name of the project).
I cannot understand what's wrong with my lambda.
Obviously covariance and contravariance and antivariance and whatnot are working OK, it seems to be just a problem with my lambda.
To be the boring person who gives the answer "because that's what the spec says" (TL;DR at the end with my thoughts)...
Basically, it's because method group conversions (e.g. assigning a method to a delegate) and anonymous function conversions (e.g. assigning a lambda to a delegate) follow different rules, and only the former benefit from variance.
(Note that a
Method Groupmeans a group of 1 or more overloads of the same method - so your single methods still count as individual method groups)Section 6.5 of the C# Language Specification talks about Anonymous function conversions:
Section 6.6 however talks about Method group conversions:
So the method group -> delegate conversion uses more or less the same rules as if you had tried to invoke the method with the corresponding parameter types. We're directed to Section 7.6.5.1, which directs us to Section 7.5.3.1. This gets complex, so I'm not going to paste it verbatim here.
Interestingly, I couldn't find the section on delegate covariance, only interface covariance (although section 6.6. says mention it in an example).
TL;DR, when you write:
the compiler goes through a whole algorithm to pick a suitable member of the method group which is compatible with the delegate type, following much the same rules as overload resolution if you were invoking the method.
When you write:
the compiler follows a much simpler rule of "does the lambda's signature match the delegate signature".
I suppose this makes sense. Most of the time you're going to be writing:
and it's up to the compiler to figure out the parameter types from the delegate signature. If you add in explicit types yourself, that doesn't completely change the algorithm used: the compiler uses the same algorithm, but if the types it comes up with conflicts with your explicit types, you get an error.
Note that almost all of the time this doesn't matter. It's rare to put types on a lambda's parameters, so you'd normally just write this:
The compiler infers that
xactually aSecond, and that's fine: if you've written a lambda which can work ifxis aFirst, it should also work ifxis aSecond(LSP notwithstanding). Notice that you're allowed to get away with returning aSecondeven thoughSampleDelegatereturns a first: the compiler doesn't mind.