Стоит отметить, что концептуально все типы делегатов теоретически могут быть по своей природе ковариантными с любым параметром типа, используемым только в качестве возвращаемого типа, и контравариантным относительно параметров типа, используемых только для параметров метода, передаваемых по значению, и компиляторы могут автоматически разрешатьдля такой дисперсии, за исключением одной проблемы: в то время как объявление in
для Action
предотвратит писк компилятора, если Action<Animal>
передается методу, который ожидает Action<Cat>
, некоторые методы ожидают Action<Cat>
может вести себя очень плохо, если дано Action<Animal>
.В общем случае, методы, которые должны принимать делегатов типов только со спецификаторами ковариации / контравариантности, если они будут корректно работать со всеми такими делегатами;в противном случае они должны принимать типы делегатов без таких спецификаторов.
Большинство методов, которые принимают Action<Cat>
, будут прекрасно работать с Action<Animal>
, поэтому Microsoft решила задним числом сделать Action<T>
контравариантным,Поскольку многие методы, которые принимают EventHandler<T>
, могут очень сильно потерпеть неудачу, если дано что-либо, что не полностью соответствует ожидаемому типу, EventHandler<T>
не был сделан контравариантным.
В ретроспективе, если каждый тип делегата определил егособственный метод Combine
, было бы возможно заставить ковариацию делегата и контравариантность работать почти во всех случаях.Если CatEventArgs:AnimalEventArgs
, сказав
EventHandler<CatEventArgs> myEvents=null;
void AddEvent(EventHandler<CatEventArgs> newEvent)
{
myEvents = EventHandler<CatEventArgs>.Combine(myEvents, newEvent);
}
, можно было бы превратить переданный EventHandler<AnimalEventsArgs>
в EventHandler<CatEventArgs>
, который затем можно было бы объединить с любым другим делегатом, который также мог бы быть преобразован в EventHandler<CatEventArgs>
,К сожалению, поскольку Combine
был определен только для Delegate
, нет способа, с помощью которого метод Combine
может узнать, какой тип делегата необходим вызывающему коду [IMHO, даже без ковариации / контравариантности было бы неплохопусть делегаты определяют свои собственные методы Combine
и Remove
, поскольку это позволило бы избежать необходимости типизации результата Delegate.Combine
].