Регистрация события, почему код компилируется без ошибок?

Я считаю, что должно быть много вопросов по SO, связанных с этим, но я не могу придумать подходящие ключевые слова для их поиска.

Код

class Subscriber
{
    public Subscriber(Notifier n)
    {
        n.OnSomeEvent += (object sender, EventArgs e) => { }; //This does not compile
        n.OnSomeEvent += ReactOnEvent; //This compile successfully!
    }

    private void ReactOnEvent(object sender, EventArgs e)
    {

    }
}

class Notifier
{
    public event EventHandler<MyEventArgs> OnSomeEvent;
    public void Trigger()
    {
        OnSomeEvent?.Invoke(this, new MyEventArgs());
    }
}

class MyEventArgs : EventArgs
{

}

Ошибки компиляции для регистрации лямбда-выражения имеют смысл.

Ошибка CS1661 Не удается преобразовать лямбда-выражение в тип делегата «EventHandler‹MyEventArgs>», поскольку типы параметров не соответствуют типам параметров делегата.

Ошибка CS1678 Параметр 2 объявлен как тип «System.EventArgs», но должен быть «ConsoleApplication1.MyEventArgs».

Но почему компилятор не дает мне те же ошибки для этого? Является ли мое понимание обработки событий в корне неверным? Я думаю, что компилятор требует, чтобы сигнатура события и его обработчиков совпадали.

n.OnSomeEvent += ReactOnEvent;

Изменить: ответ на этот вопрос

Как указал Питер в своих комментариях и процитировал ms docs,

(начиная с .NET 3.5)... вы можете назначать делегатам не только методы с совпадающими сигнатурами, но и методы, которые возвращают больше производных типов (ковариация) или принимают параметры, которые имеют меньше производных типов (контравариантность), чем указано в тип делегата.


person kennyzx    schedule 09.06.2017    source источник


Ответы (1)


при использовании лямбда-выражения входные параметры определяются компилятором.

n.OnSomeEvent += (отправитель, e) => { };

наведите указатель мыши на отправителя и e, и вы должны заметить, что они имеют правильные типы, которые требуются для OnSomeEvent.

Краткий пример использования лямбда-выражений для подписки на события можно найти здесь: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/how-to-use-lambda-expressions-outside-linq

person Aleksa    schedule 09.06.2017
comment
Ваш ответ неприменим к вопросу (поскольку код ОП не использует вывод типа параметра... он явно объявляет типы лямбда-параметров), и потому что вы не смогли на самом деле ответить на вопрос (ваш ответ не объясняет, почему анонимные методы обрабатываются иначе, чем именованные методы) - person Peter Duniho; 09.06.2017
comment
да .. Я пишу код таким образом, чтобы было ясно, что типы параметров идентичны в двух подходах. - person kennyzx; 09.06.2017
comment
@kennyzx: нет, вы не писали код таким образом, просто чтобы было понятно. Если бы вы явно не указали типы параметров, анонимный метод скомпилировался бы просто отлично, в первую очередь устраняя необходимость в вашем вопросе. Суть проблемы в вашем коде заключается в том, что вы явно указываете неправильные типы, когда для этого нет веской причины. - person Peter Duniho; 09.06.2017
comment
@PeterDuniho Спасибо за ваше объяснение, меня больше интересует, почему 2-я строка может успешно скомпилироваться. Является ли мое понимание обработки событий в корне неверным? Я думаю, что компилятор требует, чтобы сигнатура события и его обработчики совпадали. - person kennyzx; 09.06.2017
comment
@kennyzx: как описано на родственном языке спецификации, упомянутом в отмеченных дубликатах и ​​в других местах документации, C # поддерживает дисперсию для преобразований групп методов в типы делегатов. Компилятор требует, чтобы делегат, подписанный на событие, совпадал, но он позволяет использовать метод с совместимой сигнатурой для создания этого экземпляра делегата, даже если типы не совпадают точно. См., например. docs.microsoft. com/en-us/dotnet/csharp/programming-guide/ - person Peter Duniho; 09.06.2017
comment
@PeterDuniho Спасибо! Теперь я понимаю. Мне нужно время, чтобы провести эксперимент. - person kennyzx; 09.06.2017