2011-02-11 39 views
5

Acabo de encontrar un error en el programa que estoy escribiendo donde se lanzó una excepción que indica que "la referencia del objeto debe establecerse en una instancia de un objeto". Tras la investigación, descubrí que esta excepción se produjo al intentar activar un evento PERO el evento no tenía ningún método delegado.Disparar un evento en C# sin métodos de delegado adjuntos?

Quería asegurarme de que entendí que, como desarrollador, no debe desencadenar eventos sin antes comprobar que el evento no es igual a nulo? Por ejemplo:

if (this.MyEventThatIWantToFire != null) 
{ 
    this.MyEventThatIWantToFire(); 
} 

Gracias de antemano por los consejos/ideas.

+1

Estás en lo correcto. – e36M3

+1

Sí, tu comprensión es correcta. – btlog

+0

Además, en una aplicación multiproceso, almacenaría 'this.MyEventThatIWantToFire' en una variable temporal antes de verificar y, finalmente, elevarlo para evitar que se desenganchen los eventos entre su cheque y su aumento. – deepee1

Respuesta

10

El patrón que ha mostrado está roto en un entorno de subprocesos múltiples. MyEventThatIWantToFire podría ser nulo después de la prueba pero antes de la invocación. Aquí hay un enfoque más seguro:

EventHandler handler = MyEventThatIWantToFire; 
if (handler != null) 
{ 
    handler(...); 
} 

Tenga en cuenta que a menos que utilice alguna especie de barrera de la memoria, no hay garantía de que verá el último conjunto de abonados, incluso haciendo caso omiso de la condición de carrera obvia.

Pero sí, a menos que sepa que no será nulo, debe realizar un control o utilizar un método de ayuda para realizar el control por usted.

Una forma de asegurarse de que siempre hay un suscriptor es agregar usted mismo un suscriptor no operativo, p.

public event EventHandler MyEventThatIWantToFire = delegate {}; 

Por supuesto, los acontecimientos no han a ser implementado con campos de delegados simples. Por ejemplo, podría tener un evento respaldado por un List<EventHandler> en su lugar, utilizando una lista vacía para comenzar. Sin embargo, eso sería bastante inusual.

Un evento en sí nunca es nulo, porque el evento en sí es solo un par de métodos (agregar/eliminar). Ver mi article about events and delegates para más detalles.

+0

skeeeeeet. en este punto, es probable que tenga scripts configurados para responder automáticamente a este tipo de preguntas con un anuncio publicitario. – x0n

+1

¡Muchas gracias a todos por la ayuda! –

0

Sí en C# si un evento no tiene delegados, será nula, por lo que debe comprobar

0

Sí, debe comprobar si caso no es nulo. En su mayoría se encuentra con un método protegido que hace el evento de verificación e invocación, o incluso construye eventos args. Algo como esto:

public event EventHandler Foo; 

protected void OnFoo() { 
    if (Foo == null) 
     return; 

    Foo(this, new EventArgs()); 
} 

Este enfoque también alows sus clases derivadas para invocar (o prevenir la invocación) del evento.

+1

este enfoque no es seguro para subprocesos. El controlador puede cambiar entre el momento en que prueba nulo y cuándo lo llama, probablemente durante una condición de carrera. Es mejor almacenar el evento en una variable y luego probar null. –

4

No debe hacerlo de esa manera: si alguien eliminara a un oyente entre la llamada a la llamada y el evento, obtendría una excepción. Es una buena práctica para usarlo con una variable local:

protected void RaiseEvent() 
{ 
    var handler = Event; 

    if(handler != null) 
     handler(this, EventArgs.Empty); 
} 

Por supuesto, esto tiene la desventaja de que tal vez llamar a un oyente que ya se ha retirado entre la creación de la variable local y la llamada al controlador.

3

Sí, debe comprobarlo para nulo, pero la forma en que lo hace conduce a una condición de carrera.Es posible que el evento termine siendo nulo de todos modos si alguien anula la suscripción del último delegado entre su "si" y su evento. La forma aceptada de hacer esto es como:

var temp = this.MyEventThatIWantToFire; 
if (temp != null) { 
    this.temp(this, EventArgs.Empty); 
} 

o, alternativamente, asegúrese de que siempre hay al menos un delegado en la lista de invocación al declarar su evento como este: sentido

public event EventHandler MyEventThatIWantToFire = delegate {}; 

Hacer?

1

Sí, su entendimiento es el correcto. Depende de usted verificar el valor del delegado antes de llamarlo.

La mayoría de los delegados, que son eventos, son delegados de "multidifusión". Esto significa que un delegado puede administrar una lista de uno o más métodos a los que llamará cuando se activen.

Cuando el delegado evalúa a un valor null, no hay métodos registrados; por lo tanto, no hay nada que llamar. Si se evalúa como algo distinto de un valor null, hay al menos un método registrado. Llamar al delegado es una instrucción para llamar a todos los métodos que ha registrado. Los métodos se llaman sin orden garantizado.

El uso del operador de asignación de suma (+=) o el operador adicional (+) agrega un método a la lista de métodos del delegado. El uso del operador de asignación de resta (-=) o el operador de resta (-) elimina un método de la lista de métodos del delegado.

También tenga en cuenta que los métodos llamados se ejecutan desde el hilo de la persona que llama. Esto es importante si está utilizando eventos para actualizar los controles de su interfaz de usuario. En esta situación, el uso de la propiedad InvokeRequired de su control le permite manejar llamadas cruzadas (usando Invoke() o BeginInvoke()) con elegancia.

Cuestiones relacionadas