2012-04-23 13 views
12

mayor parte del código que he visto utiliza la siguiente manera de declarar e invocar evento de disparo:¿Es esta una mejor forma de activar/invocar eventos sin una comprobación nula en C#?

public class MyExample 
{ 
    public event Action MyEvent; // could be an event EventHandler<EventArgs>, too 

    private void OnMyEvent() 
    { 
     var handler = this.MyEvent; // copy before access (to aviod race cond.) 
     if (handler != null) 
     { 
      handler(); 
     } 
    } 

    public void DoSomeThingsAndFireEvent() 
    { 
     // ... doing some things here 
     OnMyEvent(); 
    } 
} 

Incluso ReSharper genera un método de invocación de la manera mencionada anteriormente.

¿Por qué no hacerlo de esta manera:

public class MyExample 
{ 
    public event Action MyEvent = delegate {}; // init here, so it's never null 

    public void DoSomeThingsAndFireEvent() 
    { 
     // ... doing some things here 
     OnMyEvent(); // save to call directly because this can't be null 
    } 
} 

¿Puede alguien explicar por qué no una razón para hacer esto? (pro vs. contras)

+0

Escuché que Jon Skeet lo hace en ambos sentidos ... –

+0

Creo que en la mayoría de los casos no es importante llamar la atención si hay al menos un oyente "real" conectado ... como se ve arriba, ya existe una verificación nula si se usa de la manera "normal". – Beachwalker

+0

posible duplicado de [¿Hay alguna desventaja al agregar un delegado vacío anónimo en la declaración de evento?] (Http://stackoverflow.com/questions/170907/is-there-a-downside-to-adding-an-anonymous-empty -delegate-on-event-declaration) – CodesInChaos

Respuesta

16

Los pros y los contras son:

  • cheques nulos son muy baratos; estamos hablando milmillonésimas de segundo. La asignación de un delegado y luego la recolección de basura sin éxito durante el resto de la vida útil del objeto podría llevar quizás más de millonésimas de segundo. Además, está consumiendo docenas de bytes más memoria. Además, cada vez que se dispara el evento, se obtiene una llamada innecesaria a un método que no hace nada, consumiendo incluso más microsegundos. Si usted es el tipo de persona que se preocupa por millonésimas de segundo y decenas de bytes, eso podría ser una diferencia significativa; para la gran mayoría de los casos, no lo hará.

  • Debe recordar siempre crear el delegado vacío. ¿Es eso realmente más fácil que recordar comprobar nulo?

  • Ninguno de los dos patrones realmente hace que el evento sea seguro. Todavía es completamente posible con ambos patrones que un controlador de eventos se active en un hilo mientras se elimina en otro hilo, y eso significa que compiten. Si el código de eliminación de controlador destruye el estado que el controlador necesita, entonces es posible que un hilo esté destruyendo ese estado mientras otro hilo ejecuta el controlador. No crea que la mera verificación de la nulidad o la asignación mágica de un controlador vacío eliminan las condiciones de carrera. Solo elimina la condición de anticipación que ocasiona la desreferenciación nula.

+3

No solo se agrega una memoria de almacenamiento de controlador vacía en el caso en que no se utiliza ningún controlador, sino que lo que es más importante, hace que agregar y eliminar el primer controlador real * mucho * sea más costoso. Agregar el primer controlador a un evento es muy barato: simplemente reemplace el nulo con el controlador. Eliminar el único controlador de un evento es barato: reemplázalo por nulo. Todos los demás casos son mucho más caros en comparación. – supercat

+0

Sí, un cheque nulo es rápido, pero ¿no es la copia del controlador antes del cheque una operación costosa? Se realiza con cada evento de disparo llamando a OnMyEvent(). Ver código var handler = this.MyEvent; (en el ejemplo) ¿Hay alguna comparación práctica de "vida real" disponible? – Beachwalker

+1

@Stegi: la copia es completamente gratis. Una vez que se carga en el registro, simplemente se guarda allí. (Al menos en código optimizado) – configurator

5

Es puede seguir siendo nulo. Consideremos:

var x = new MyExample(); 
    x.MyEvent += SomeHandler; 

    // ... later, when the above code is disposed of 

    x.MyEvent -= SomeHandler; 

EDIT: realidad, lo retiro. Después de haber probado esto, si ha utilizado un delegado anónimo para establecer el controlador, no parece que puede borrarlo, por lo que puede guardar la verificación nula.

No estoy seguro de si esto es un comportamiento fiable sin embargo, o simplemente un artefacto de la implementación del lenguaje ...

6

es una cosa de estilo a ciencia cierta; sin embargo, creo que la mayoría de los desarrolladores optan por la seguridad sobre el estilo en lo que respecta a los controles nulos. No hay nada en este mundo que pueda garantizar que un error no se infiltre en el sistema y que la verificación nula seguirá siendo innecesaria.

Cuestiones relacionadas