Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

183
Vistas
¿Por qué TEventArgs no se hizo contravariante en el patrón de eventos estándar en el ecosistema .NET?

Al aprender más sobre el modelo de eventos estándar en .NET, descubrí que antes de introducir genéricos en C#, el método que manejará un evento está representado por este tipo de delegado:

 // // Summary: // Represents the method that will handle an event that has no event data. // // Parameters: // sender: // The source of the event. // // e: // An object that contains no event data. public delegate void EventHandler(object sender, EventArgs e);

Pero después de que se introdujeron los genéricos en C# 2, creo que este tipo de delegado se reescribió usando genericidad:

 // // Summary: // Represents the method that will handle an event when the event provides data. // // Parameters: // sender: // The source of the event. // // e: // An object that contains the event data. // // Type parameters: // TEventArgs: // The type of the event data generated by the event. public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);

Tengo dos preguntas aquí:

Primero, ¿por qué el parámetro de tipo TEventArgs no se hizo contravariante ?

Si no me equivoco, se recomienda hacer que los parámetros de tipo que aparecen como parámetros formales en la contravariante de la firma de un delegado y el parámetro de tipo que será el tipo de retorno en la covariante de la firma del delegado.

En el libro de Joseph Albahari, C# en pocas palabras, cito:

Si está definiendo un tipo de delegado genérico, es una buena práctica:

  • Marque un parámetro de tipo usado solo en el valor de retorno como covariante (fuera).
  • Marque cualquier parámetro de tipo usado solo en parámetros como contravariante (in).

Hacerlo permite que las conversiones funcionen de forma natural al respetar las relaciones de herencia entre los tipos.

Segunda pregunta: ¿Por qué no hubo una restricción genérica para hacer cumplir que los TEventArgs derivan de System.EventArgs ?

Como sigue:

 public delegate void EventHandler<TEventArgs> (object source, TEventArgs e) where TEventArgs : EventArgs;

Gracias por adelantado.

Editado para aclarar la segunda pregunta:

Parece que la restricción genérica en TEventArgs ( donde TEventArgs: EventArgs ) estaba allí antes y Microsoft la eliminó, por lo que aparentemente el equipo de diseño se dio cuenta de que no tenía mucho sentido práctico.

Edité mi respuesta para incluir algunas de las capturas de pantalla de

fuente de referencia .NET

ingrese la descripción de la imagen aquí

over 4 years ago · Santiago Trujillo
1 Respuestas
Responde la pregunta

0

En primer lugar, para abordar algunas inquietudes en los comentarios a la pregunta: generalmente rechazo con fuerza las preguntas "por qué no" porque es difícil encontrar razones concisas por las que todos en el mundo eligieron no hacer este trabajo , y porque todo el trabajo no es hecho por defecto . Más bien, hay que encontrar una razón para hacer el trabajo y quitarle recursos a otros trabajos que son menos importantes para hacerlo.

Además, las preguntas de "por qué no" de esta forma, que indagan sobre las motivaciones y elecciones de las personas que trabajan en una empresa en particular, pueden ser respondidas solo por las personas que tomaron esa decisión, que probablemente no estén aquí.

Sin embargo, en este caso podemos hacer una excepción a mi regla general de cerrar las preguntas "por qué no" porque la pregunta ilustra un punto importante sobre la covarianza de los delegados sobre el que nunca he escrito antes.

No tomé la decisión de mantener los delegados de eventos sin variantes, pero si hubiera estado en condiciones de hacerlo, habría mantenido los delegados de eventos sin variantes, por dos razones.

El primero es puramente un punto de "fomentar las buenas prácticas". Los controladores de eventos generalmente están diseñados específicamente para manejar un evento en particular, y no hay una buena razón que conozca para que sea más fácil de lo que ya es usar delegados que tienen discrepancias en la firma como controladores, incluso si esas discrepancias pueden ser tratado a través de la varianza. Un controlador de eventos que coincida exactamente en todos los aspectos con el evento que se supone que debe manejar me da más confianza de que el desarrollador sabe lo que está haciendo al construir un flujo de trabajo basado en eventos.

Esa es una razón bastante débil. La razón más fuerte es también la razón más triste.

Como sabemos, los tipos de delegados genéricos se pueden hacer covariantes en sus tipos de devolución y contravariantes en sus tipos de parámetros; normalmente pensamos en la variación en el contexto de la compatibilidad de asignaciones. Es decir, si tenemos una Func<Mammal, Mammal> en la mano, podemos asignarla a una variable de tipo Func<Giraffe, Animal> y saber que la función subyacente siempre tomará un mamífero, porque ahora solo obtendrá jirafas, y siempre devolverá un animal, porque devuelve mamíferos.

Pero también sabemos que los delegados pueden sumarse; los delegados son inmutables, por lo que sumar dos delegados produce un tercero; la suma es la composición secuencial de los sumandos.

Los eventos similares a campos se implementan mediante la suma de delegados; es por eso que agregar un controlador a un evento se representa como += . (No soy un gran admirador de esta sintaxis, pero ahora estamos atascados con ella).

Aunque ambas características funcionan bien de forma independiente, funcionan mal en combinación. Cuando implementé la varianza de delegados, nuestras pruebas descubrieron en poco tiempo que había una serie de errores en CLR con respecto a la adición de delegados en los que los tipos de delegados subyacentes no coincidían debido a las conversiones habilitadas para la varianza. Estos errores habían estado allí desde CLR 2.0, pero hasta C# 4.0, ningún lenguaje convencional había expuesto los errores, no se habían escrito casos de prueba para ellos, etc.

Lamentablemente, no recuerdo cuáles eran los reproductores de los bichos; fue hace doce años y no sé si aún conservo alguna nota guardada en algún disco en alguna parte.

Trabajamos con el equipo de CLR en ese momento para tratar de corregir estos errores para la próxima versión de CLR, pero no se consideraron lo suficientemente prioritarios en comparación con su riesgo. Muchos tipos como IEnumerable<T> e IComparable<T> , etc., se hicieron variantes en esos lanzamientos, al igual que los tipos Func y Action , pero es raro sumar dos Func que no coinciden usando una conversión de variante . Pero para los delegados de eventos, su único propósito en la vida es sumar; se agregarían todo el tiempo, y si hubieran sido variantes, habría existido el riesgo de exponer estos errores a una gran cantidad de usuarios.

Perdí la pista de los problemas poco después de C# 4 y, sinceramente, no sé si alguna vez se abordaron. ¡Intente sumar algunos delegados que no coincidan en varias combinaciones y vea si sucede algo malo!

Así que esa es una buena pero desafortunada razón por la que no hacer que los delegados de eventos sean una variante en el marco de tiempo del lanzamiento de C# 4.0. Si todavía hay una buena razón, no lo sé. Tendrías que preguntarle a alguien del equipo de CLR.

over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda