2010-04-19 12 views
6

Mi clase con un evento:eventos de la convención - Yo no lo entiendo

public class WindowModel 
{ 
    public delegate void WindowChangedHandler(object source, WindowTypeEventArgs e); 
    public event WindowChangedHandler WindowChanged; 

    public void GotoWindow(WindowType windowType) 
    { 
     this.currentWindow = windowType; 
     this.WindowChanged.Invoke(this, new WindowTypeEventArgs(windowType)); 
    } 
} 

Derivado clase de evento:

public class WindowTypeEventArgs : EventArgs 
{ 
    public readonly WindowType windowType; 

    public WindowTypeEventArgs(WindowType windowType) 
    { 
     this.windowType = windowType; 
    } 
} 

alguna otra clase que se registran al evento:

private void SetupEvents() 
{ 
    this.WindowModel.WindowChanged += this.ChangeWindow; 
} 

private void ChangeWindow(object sender, WindowTypeEventArgs e) 
{ 
    //change window 
} 

¿Qué obtuve al seguir la convención .Net? Tendría más sentido tener un contrato como éste

public delegate void WindowChangedHandler(WindowType windowType); 
public event WindowChangedHandler WindowChanged; 

Hacerlo de esta manera, no necesito para crear una nueva clase y es más fácil de entender. No estoy codificando una biblioteca .Net. Este código solo se usará en este proyecto. Me gustan las convenciones, pero ¿tengo razón cuando digo que en este ejemplo no tiene sentido o no he entendido bien algo?

Respuesta

15

Visto aisladamente, sí, tiene usted razón: la sintaxis convencional .NET es más detallado y menos intuitiva, pero hay ventajas:

  • futuros cambios a la información que pasa por su evento no forma automática requieren cambios a todos los consumidores del evento. Por ejemplo, si desea agregar una información adicional a su evento, por ejemplo, una cadena WindowTitle, tendrá que modificar la firma de cada función que se adjunta a ese evento, independientemente de si usarlo Con el enfoque EventArgs, agrega la propiedad a los argumentos y solo altera las funciones que necesitan aprovechar la información adicional.
  • Desde que .NET 2.0 introdujo el tipo de delegado EventHandler<TEventArgs>, ya no necesita definir sus propios delegados de evento manualmente. En su ejemplo, debe escribir su evento como EventHandler<WindowTypeEventArgs> en lugar de WindowChangedHandler.
  • El enfoque EventArgs hace que sea fácil pasar múltiples tipos de información a la función de llamada. Si necesita hacer esto en su ejemplo alternativo (que pasa el parámetro de evento directamente), igual podría terminar creando su propia clase -tuple para contener la información.

El impacto de la primera de ellas se hace más evidente cuando nos fijamos en la real patrón para.NET eventos en la creación de una función protected virtual que realmente hace la invocación. Por ejemplo:

public event EventHandler<WindowTypeEventArgs> WindowChanged; 

protected virtual void OnWindowChanged(WindowTypeEventArgs e) 
{ 
    var evt = WindowChanged; 

    if(evt != null) evt(this, e); 
} 

Hay un par de cosas que me gustaría señalar aquí:

  1. Utilizando el patrón de creación de este método de evento-invocando le permite evitar la comprobación nula en todo el código (un evento sin funciones adjuntas será null y lanzará una excepción si intenta invocarlo)
  2. Este patrón también permite que las clases que hereden de usted controlen el orden de invocación, permitiéndoles ejecutar su código explícitamente antes o después de cualquier consumidor externo
  3. Esto es especialmente importante en entornos multiproceso. Si acaba de decir if(WindowChanged != null) WindowChanged(this, e);, correría el riesgo de que el evento WindowChanged se convierta en null entre el momento en que lo comprueba y el momento en que lo llama. Esto no es importante en escenarios de un solo subproceso, pero es un gran hábito defensivo para formar.
+0

Me gusta mucho esta respuesta! El primer punto es tan obvio, pero algo en lo que no pensé. Segundo punto que no sabía pero es muy bueno :) No estoy seguro de entender tu tercer punto. ¡Gracias! –

+0

@bobjink: ¡Me alegro de que sea útil! Mi tercer punto fue sobre poder enviar datos de vuelta al objeto que llama al evento. Usando el enfoque 'EventArgs', puede permitir que el consumidor de su evento modifique una propiedad en él, como en la clase' CancelEventArgs', lo que le permitirá pasar datos nuevamente a usted. –

+0

¡Ya veo! Gracias :) –

1

Puede usar su delegado. Nadie te forzará. Es solo un buen patrón para eventos.

Si utiliza el patrón standart Sender-EventArgs, también podrá usar el mismo manejador ChangeWindow para otros eventos.

3

Reconozco tu confusión! Tuve la misma sensación cuando miré esto por primera vez.

Lo importante es darse cuenta de que no le da una gran ventaja programática, pero es una convención que es bien conocida en el marco. Como tal, hay muchas herramientas que esperan la firma void EventName(object sender, EventArgs e). Algunos contenedores IoC, por ejemplo, pueden usar esta firma para automatizar automáticamente eventos en tiempo de construcción.

En resumen, se ve un poco raro, pero es una convención. Quédese con él y la bombilla se iluminará eventualmente!

Cuestiones relacionadas