Forma preferida de declarar eventos

14

Estoy bastante contento con mi comprensión del modelo de eventos .NET. Creo que podría estar malinterpretando un pequeño matiz del sistema.

Cuando comencé a poner eventos en mis clases, usaría la forma estándar de esta manera:

public event EventHandler<MyEventArgs> MyEvent;

Esto significaba que cualquier cosa que se suscribiera al evento necesitaría un método como:

void HandleThatEvent(object sender, MyEventArgs args){...}

Lo cual es bueno, pero descubrí que rara vez me importaría el remitente, por lo que hizo que se hincharan muchas firmas de métodos.

Entonces cambié a declarar mis propios tipos de delegado

public delegate void MyEventHandler(SomeClass argument);

Lo que redujo el desorden, pero me dejó con un pequeño problema a la hora de escribir controladores:

eventImplmentor.MyEvent += HandleThatEvent;
.
.
.
void HandleThatEvent(/*oh, um, what arguments does it take? Intellisense isn't telling me*/)

Así que tendría que volver a la declaración del delegado y mirar y luego regresar y escribirlos, o compilarlo y esperar a que me lo digan.

Entonces, en cambio, solo estoy usando Action, Action<T>o cualquier plantilla que se ajuste.

public event Action<SomeClass> MyEvent;

Para que pueda pasar el cursor sobre el evento y que me digan qué parámetros espera.

Mi pregunta, después de todo eso: ¿hay una mejor práctica para declarar eventos en C #? ¿Debo volver al EventHandler<T>camino, o es Action<T>aceptable?

Matt Ellen
fuente
No olvides asegurarte de que el controlador se copia localmente en el lugar donde disparas el evento, siempre debes hacerlo para la seguridad del subproceso.
Snoop
Puede escribir sus propios eventos seguros de tipo inteligente, medio y delgado en código encapsulado, pero todo lo que publique debe seguir el patrón estándar o simplemente confundirá a los usuarios de su clase (y aparentemente algunas herramientas también).
Martin Maat

Respuestas:

8

Para el manejo simple e interno de eventos, existen aquellos que simplemente usan Actiono Action<T>, como está proponiendo. Tiendo a usar el patrón estándar, incluido el remitente, incluso para eventos internos, porque nunca se sabe cuándo es posible que luego desee exponer una clase o evento, y no me gustaría la pena de tener que refactorizar el método del evento solo para hacer Es público.

Estoy de acuerdo con usted en que la firma de manejo de eventos es un poco más pesada de lo que debería ser para escenarios simples, pero está bien diseñada para manejar la migración incremental ya que los argumentos de eventos adicionales podrían ser necesarios con el tiempo. En general, me quedaría con el patrón estándar, especialmente porque, como notó, solo obtiene el soporte IntelliSense adecuado si lo hace.

Para lo que vale, puse algo de tiempo en esto y se me ocurrió un patrón de manejo de eventos diferente: ¿ Firma del evento en .NET - Usando un 'Remitente' de tipo fuerte? . El objetivo aquí no era eliminar al remitente, sino hacerlo genéricamente tipeado fuerte en TSenderlugar de tipeado débilmente System.Object. Funciona muy bien; sin embargo, uno pierde el soporte de IntelliSense cuando hace esto, por lo que hay una desafortunada compensación.

En general, me apegaría al patrón estándar, pero es interesante pensar en formas potencialmente mejores de hacerlo.

Mike Rosenblum
fuente
Gracias por señalarme tu pregunta SO. Es muy interesante. Todavía no entiendo por qué es tan importante que el remitente sea obligatorio. La mayoría de las veces no me importa el remitente. ¿Es solo una regla arbitraria de MS?
Matt Ellen
No, por supuesto, puede declarar a sus delegados como quiera. Es política de .NET incluir siempre al remitente, y no es del todo una mala idea.
Neil
@Neil: Entiendo que a veces es útil, pero no entiendo la política de hacerlo siempre, especialmente porque MS recomienda hacer los eventos a su manera. Una de las cosas que realmente me gustan de los eventos es la capacidad de desacoplar clases. Si incluyo el objeto, se vuelve a acoplar nuevamente. Si es solo una cuestión de cumplimiento de CLS, entonces puedo vivir con eso.
Matt Ellen el
Se vuelve a acoplar nuevamente si usa el objeto remitente; de ​​lo contrario, no importa cuál sea el valor del remitente, ya que no lo usa. La dependencia existe solo si necesita que haya una dependencia. Veo de dónde vienes, y si el emisor de objetos desapareciera de todo el código de cualquier servidor del planeta, no me quedaría despierto por las noches.
Neil
Sí, puede enviar 'nulo' como remitente si realmente lo desea ... Pero, al incluir al remitente, el controlador de eventos podría darse de baja si así lo desea. Sin embargo, en general, diría que conocer la fuente del evento suele ser bastante importante.
Mike Rosenblum