2008-12-04 12 views
65

No es posible desencadenar un evento en C# que no tiene controladores asociados. Entonces, antes de cada llamada, es necesario verificar si el evento es nulo.Crear controladores de eventos C# vacíos automáticamente

if (MyEvent != null) { 
    MyEvent(param1, param2); 
} 

Me gustaría mantener mi código lo más limpio posible y deshacerme de esos controles nulos. No creo que afecte mucho el rendimiento, al menos no en mi caso.

MyEvent(param1, param2); 

Ahora resuelvo esto agregando manualmente un controlador en línea vacío para cada evento. Esta es propenso a errores, ya que necesito recordar a hacer eso etc.

void Initialize() { 
    MyEvent += new MyEvent((p1,p2) => { }); 
} 

¿Hay una manera de generar manipuladores vacíos para todos los eventos de una clase dada de forma automática mediante la reflexión y un poco de magia CLR?

+0

el truco en la respuesta aceptada evitará tener que comprobar la nula pero no será asegurar safty hilo. mira aquí: http://stackoverflow.com/questions/1131184/c-initializing-an-event-handler-with-a-dummy/1131204#1131204 –

Respuesta

135

Vi esto en otro post y descaradamente han robado y lo utilizaron en gran parte de mi código desde entonces:

public delegate void MyClickHandler(object sender, string myValue); 
public event MyClickHandler Click = delegate {}; // add empty delegate! 

//Let you do this: 
public void DoSomething() { 
    Click(this, "foo"); 
} 

//Instead of this: 
public void DoSomething() { 
    if (Click != null) // Unnecessary! 
     Click(this, "foo"); 
} 

* Si alguien conoce el origen de esta técnica, por favor, publicarlo en los comentarios . Realmente creo en la fuente obteniendo el debido crédito.

(Editar: Lo obtuve de este post Hidden Features of C#?)

+2

¡24 segundos más rápido! – leppie

+3

¡Agregue un delegado vacío, allí mismo! Eso es incluso mejor de lo que esperaba. ¡Gracias! Voy a leer la publicación de "funciones ocultas" en este momento. –

+0

Sí - ¡Esa publicación no tiene precio! Asegúrese de votar a menudo allí. Nos han hecho a todos un gran servicio. – Dinah

6

Se puede escribir como es:

MyEvent += delegate { }; 

No estoy seguro de lo que quieres hacer es correcta.

+0

Realmente creo, que agregar delegado vacío a CADA evento es la forma correcta, cómo desarrollar aplicaciones. Pero creo que no puede haber ninguna situación, ¿dónde está la solución rápida y fácil de cómo manejar algo? – TcKs

57

La notación:

if (MyEvent != null) { 
    MyEvent(param1, param2); 
} 

no es seguro para subprocesos. Debe hacerlo de esta manera:

EventHandler handler = this.MyEvent; 
if (null != handler) { handler(param1, param2); } 

entiendo, que esto es una molestia, por lo que puede hacer método de ayuda:

static void RaiseEvent(EventHandler handler, object sender, EventArgs e) { 
    if (null != handler) { handler(sender, e); } 
} 

y luego llamar a:

RaiseEvent(MyEvent, param1, param2); 

Si están usando C# 3.0, puede declarar el método auxiliar como método de extensión:

static void Raise(this EventHandler handler, object sender, EventArgs e) { 
    if (null != handler) { handler(sender, e); } 
} 

y luego llamar a:

MyEvent.Raise(param1, param2); 

También puede crear siguiente extensión/métodos de ayuda para otros controladores de eventos. Por ejemplo:

static void Raise<TEventArgs>(this EventHandler<TEventArgs> handler, 
    object sender, TEventArgs e) where TEventArgs : EventArgs 
{ 
    if (null != handler) { handler(sender, e); } 
} 
+4

Usar un método de extensión es una solución hermosa. Me estremezco cuando se plantea la idea de inicializar un delegado vacío. – Greg

+0

Guau, pensé que '= delegate {}' era útil cuando lo vi por primera vez. Esto es +1 increíble, sin embargo. Y tan obvio en retrospectiva, maldición :) – shambulator

+0

+1 para los métodos de extensión –

2

Ésta es una mala idea porque el código que está consumiendo el evento tiene ahora una expectativa de que el objeto con el evento ha sido codificado con una acción por defecto. Si nadie va a utilizar su código en ningún otro lugar, supongo que puede salirse con la suya.

+0

Estoy de acuerdo, como he comentado en la respuesta de Leppie. +1 – TcKs

5

No necesita varios métodos de extensión para los diferentes controladores de eventos, sólo tiene uno:

public static class EventHandlerExtensions { 
    public static void Raise<T>(this EventHandler<T> handler, object sender, T args) where T : EventArgs { 
    if (handler != null) handler(sender, args); 
    } 
} 
+0

Gracias Sandro! – vkelman

-1

Usted puede utilizar PostSharp que el tiempo de construcción añadir esta magia. Es la mejor manera.

1

Las declaraciones de eventos C# lamentablemente incluyen una serie de problemas de seguridad e ineficiencias bien conocidos. I designed a number of extension methods on delegates to invoke them safely, and to register/unregister delegates in a thread-safe manner.

Su viejo código de evento de sensibilización:

if (someDelegate != null) someDelegate(x, y, z); 

Su nuevo código:

someDelegate.Raise(x, y, z); 

Su antiguo código de registro de eventos:

event Action fooEvent; 
... 
lock (someDummyObject) fooEvent += newHandler; 

Su nuevo código:

Action fooEvent; 
... 
Events.Add(ref fooEvent, newHandler); 

No se necesita bloqueo, no se han utilizado objetos ficticios insertados en el compilador para bloquear los eventos.

2

En C# 6.0 no hay necesidad de ir a cualquiera de estas longitudes de hacer el cheque nulo, gracias a que el operador nulo condicional ?.

The docs explican que llamar MyEvent?.Invoke(...) copias del evento a una variable temporal, realiza la nula check, y si no es nulo, llama al Invoke en la copia temporal. Esto no es necesariamente seguro para subprocesos en todos los sentidos, ya que alguien podría haber agregado un nuevo evento después de la copia a la variable temporal, que no se llamaría. Sin embargo, garantiza que no llamará al Invoke en nulo.

En resumen:

public delegate void MyClickHandler(object sender, string myValue); 
public event MyClickHandler Click; 

public void DoSomething() { 
    Click?.Invoke(this, "foo"); 
} 
Cuestiones relacionadas