Невозможно назначить делегат анонимным методом с менее конкретным типом параметра
Я могу назначить метод 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