2009-10-22 10 views
11

Estaba leyendo una página en events en MSDN, y encontré un fragmento de código de ejemplo que me deja perplejo.Copia de delegados

El código en cuestión es la siguiente:

// Make a temporary copy of the event to avoid possibility of 
// a race condition if the last subscriber unsubscribes 
// immediately after the null check and before the event is raised. 
EventHandler<CustomEventArgs> handler = RaiseCustomEvent; 

entiendo las intenciones del código, pero no veo cómo esa línea particular está haciendo una copia de cualquier cosa. Todo lo que hace es copiar la referencia ; en realidad no está haciendo una copia profunda de la instancia delegada. Entonces para ese fin, en realidad no previene la condición de carrera en absoluto.

¿Me falta algo obvio aquí?

+1

¿Puede RaiseCustomEvent establecerse en nulo en un subproceso diferente antes de que el método actual tenga la oportunidad de desencadenarlo? –

Respuesta

17

Los delegados son inmutables, por lo que se garantiza que la referencia obtenida en ese código no cambiará. Si un usuario se suscribe o cancela la suscripción después de la verificación nula, se creará un nuevo delegado y se establecerá en el evento. Sin embargo, como tiene una referencia a un objeto completamente diferente e invoca eso, no tiene que preocuparse de que sea nulo.

+0

Ahh bien, eso tiene mucho más sentido, ¡gracias! Tengo que decir que estoy impresionado con la rapidez con la que respondieron. – David

4

Tiene razón; está copiando la referencia.

Sin embargo, los delegados son inmutables; cuando agrega un controlador a un evento, se crea un nuevo delegado, combinando el manejador actual (es) con el nuevo, y luego se asigna al campo.

La instancia de delegado a la que hace referencia el campo no puede cambiar, por lo que evita la condición de carrera.

1

Esto es de MSDN también ..

"La lista de invocación de un delegado es un conjunto ordenado de los delegados en la que cada elemento de la lista invoca exactamente uno de los métodos representados por el delegado. Una invocación lista puede contener métodos duplicados. Durante una invocación, los métodos se invocan en el orden en que aparecen en la lista de invocación. Un delegado intenta invocar cada método en su lista de invocación; se invocan duplicados una vez por cada vez que aparecen en el lista de invocaciónLos delegados son inmutables; una vez creados, la lista de invocación de un delegado no cambia "

0

if (whatever != null) whatever(); parece que asegura que nunca se whatever es nula cuando whatever() se llama, pero en realidad no asegurar que en un escenario de rosca. Un hilo diferente puede establecer whatever = null entre el cheque y la llamada.

Foo temp = whatever; 
if (temp != null) temp(); 

Este código elimina la posibilidad de una desreferencia nula, ya que temp es un local y, por lo tanto, nunca será modificado por un hilo diferente. Por lo tanto, evita una condición de carrera, aunque no impide todas las condiciones de carrera relevantes. Eric Lippert hizo una discusión more elaborate de algún otro profesional blems con el código.