В общем объявлении делегата можно указать, что определенные параметры типа должны быть ковариантными или контравариантными, что позволит использовать типы назначений. К сожалению, внутренняя реализация делегатов многоадресной рассылки делает невозможным объединение делегатов разных типов («простой» делегат содержит информацию о своем типе, указатель метода и ссылку на его цель; многоадресная информация делегата о ее типе наряду с содержит указатели на методы и целевые ссылки для каждого из своих составных делегатов, но не содержит ссылки на исходных делегатов, которые были объединены, и не содержит никакой информации об их типах). Таким образом, попытка совместить 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>
не сможет выполнить компиляцию, даже если действие может быть успешным, если это была единственная подписка.
Я не уверен, поможет ли здесь разница. 'EventHandler <>' инвариантно определен в .NET. – nawfal