В объявлении общего делегата можно указать, что определенные параметры типа должны быть ковариантными или контравариантными, что позволит использовать типы назначений, которые вы ищете. К сожалению, внутренняя реализация многоадресных делегатов делает невозможным объединение делегатов разных типов («простой» делегат содержит информацию о своем типе вместе с указателем метода и ссылкой на его цель; информация о его типе многоадресного делегата вместе с содержит указатели на метод и целевые ссылки для каждого из составляющих его делегатов, но не содержит никаких ссылок на исходные делегаты, которые были объединены, а также не содержит никакой информации об их типах). Таким образом, попытка объединить EventHandler<DerivedEventArgs>
с EventHandler<EventArgs>
потерпит неудачу во время выполнения.
Если бы EventHandler<T>
было контравариантным по отношению к T
, попытка передать EventHandler<EventArgs>
стандартному методу AddHandler
события, которое предполагает компиляцию EventHandler<DerivedEventArgs>
, и даже была бы успешной, если бы не было других обработчиков. подписался, поскольку EventHandler<EventArgs>
будет Delegate.Combine
d с null
, таким образом, сохраняясь в поле делегата события как EventHandler<EventArgs>
. К сожалению, последующая попытка добавить EventHandler<DerivedEventArgs>
(который на самом деле является ожидаемым типом) потерпит неудачу, поскольку его тип не соответствует делегату, с которым он комбинируется. Microsoft решила, что такое поведение нарушит принцип наименьшего удивления (если передача «неправильного» делегата вызовет какие-либо проблемы, она должна делать это при передаче этого делегата, а не при передаче более позднего), и решила минимизировать вероятность сценария, сделав так, что попытка передать EventHandler<EventArgs>
обработчику, который ожидает, что EventHandler<DerivedEventArgs>
завершится компиляцией, даже если действие могло завершиться успешно, если бы это была единственная подписка.