Может быть, стоит отметить, что концептуально все типы делегатов теоретически может быть по своей сути ковариантны с любым типом параметра используется только в качестве возвращаемого типа и контрвариантным в отношении к типу параметров, используемых только для параметров методов, передаваемых по значению, и компиляторы может автоматически допускают такую дисперсию, за исключением одной проблемы: в то время как объявление 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
].