Vale la pena señalar que conceptualmente todos los tipos de delegados podrían ser intrínsecamente covariantes con cualquier parámetro de tipo utilizado únicamente como tipo de retorno y contravariante con respecto a los parámetros de tipo utilizados solo para parámetros de método pasados por valor y los compiladores podrían automáticamente permitir tal variación, excepto por un problema: mientras que la declaración in
de Action
evitará que el compilador grazne si se pasa un Action<Animal>
a un método que espera un Action<Cat>
, algunos métodos que esperan un Action<Cat>
pueden comportarse muy mal si se les da un Action<Animal>
. En general, los métodos que solo deben aceptar delegados de tipos con especificadores de covarianza/contravarianza si funcionan correctamente con todos los delegados; de lo contrario, deberían aceptar tipos de delegados sin dichos especificadores.
La mayoría de métodos que aceptar una Action<Cat>
funcionará bien con un Action<Animal>
, por lo que Microsoft decidió con carácter retroactivo para hacer Action<T>
contravariante. Debido a que muchos métodos que aceptan EventHandler<T>
pueden fallar muy gravemente si se les da algo que no coincide perfectamente con el tipo esperado, EventHandler<T>
no se hizo contravariante.
En retrospectiva, si cada tipo de delegado hubiera definido su propio método Combine
, habría sido posible hacer que la covarianza de delegado y la contravarianza funcionen en casi todos los casos. Si CatEventArgs:AnimalEventArgs
, diciendo
EventHandler<CatEventArgs> myEvents=null;
void AddEvent(EventHandler<CatEventArgs> newEvent)
{
myEvents = EventHandler<CatEventArgs>.Combine(myEvents, newEvent);
}
podrían haber resultado un pasado-en EventHandler<AnimalEventsArgs>
en un EventHandler<CatEventArgs>
, que entonces podrían combinarse con cualquier otro delegado que igualmente se podría convertir en un EventHandler<CatEventArgs>
.Desafortunadamente, dado que Combine
solo se definió en Delegate
, no hay forma de que el método Combine
sepa qué tipo de delegado necesita el código de llamada [En mi humilde opinión, incluso sin covarianza/contravarianza, hubiera sido agradable que los delegados definieran su propio Combine
y Remove
métodos, ya que eso habría evitado la necesidad de encasillar el resultado de Delegate.Combine
].