Can’t assign to delegate an anonymous method with

2019-01-09 15:23发布

问题:

I’m able to assign a method M to delegate object d with a less specific parameter type, but when I want to assign an anonymous method with same the signature as method M to d, I get an error.

Why is that?

class derivedEventArgs : EventArgs { }

delegate void newDelegate(object o, derivedEventArgs e); 

static void Main(string[] args)
{
    newDelegate d = M; // ok
                d = (object o, EventArgs e) => { }; // error
}

public static void M(object o, EventArgs e) { }

回答1:

Jared is of course correct that this is by design.

The reason for that design is that in the contravariant method conversion case, you might have a method that you didn't write, and be assigning it to a delegate variable that you didn't write either. You don't control the types. So we go a bit easy on you and let the parameters match contravariantly and the return types match covariantly.

In the lambda-to-delegate conversion, you do control the thing being assigned. There is nothing stopping you from making it an exact match in the parameter types and therefore we require you to. No fudging allowed here.



回答2:

This is covered in section 6.5 of the C# language specification. If you explicitly type the parameters of an anonymous function, they must match in both type and modifiers in order to be compatible signatures.

Specifically, a delegate type D is compatible with an anonymous function F provided

...

If F has an explicitly typed parameter list, each parameter in D has the same type and modifiers as the corresponding parameter in F.



回答3:

While you have got your answer, I will provide a workaround if this is required. Say, all you got is a delegate of signature (object, EventArgs) in which case you want to convert it to your newDelegate type, you could do:

SomeDelegate p = (object o, EventArgs e) => { }; //comes from somewhere
NewDelegate d = (o, e) => p(o, e); //can rewrite like this

Alternately with generics and (contra) variance feature of generic delegates, you can do it with one delegate type:

delegate void NewDelegate<in T>(object o, T e) where T : EventArgs;

//then
NewDelegate<EventArgs> p = (object o, EventArgs e) => { }; //comes from somewhere
NewDelegate<DerivedEventArgs> d = p; //straightforward assignable - contravariance