Невозможно назначить делегат анонимным методом с менее конкретным типом параметра

Я могу назначить метод M делегировать объект d с менее конкретным типом параметра, но когда я хочу назначить анонимный метод с той же сигнатурой, что и метод M, на d, я получаю ошибка.

Почему это?

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

Джаред, конечно, правильно, что это по дизайну.

Причиной такого дизайна является то, что в случае преобразования контравариантного метода у вас может быть метод, который вы не пишете, и назначать его переменной-делегату, которую вы также не писали. Вы не контролируете типы. Таким образом, мы немного упростим вас и пусть параметры соответствуют контравариантно, а возвращаемые типы ковариантно совпадают.

В преобразовании lambda-to-delegate вы контролируете назначаемую вещь. Нет ничего, что помешает вам сделать точное соответствие в типах параметров, и поэтому мы требуем от вас. Здесь не допускается фальсификация.

Ответ 2

Это описано в разделе 6.5 спецификации языка С#. Если вы явно вводите параметры анонимной функции, они должны соответствовать как типу, так и модификаторам, чтобы быть совместимыми сигнатурами.

В частности, тип делегата D совместим с анонимной функцией F, предоставляемой

...

Если F имеет явно типизированный список параметров, каждый параметр в D имеет тот же тип и модификаторы, что и соответствующий параметр в F.

Ответ 3

Пока вы получили свой ответ, я предоставлю обходное решение, если это потребуется. Скажем, все, что у вас есть, является делегатом подписи (object, EventArgs), и в этом случае вы хотите преобразовать его в свой тип newDelegate, вы можете сделать:

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

В качестве альтернативы с характеристиками generics и (contra) дисперсии общих делегатов вы можете сделать это с помощью одного типа делегата:

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