[c#] Comprobando nulo antes de enviar el evento ... ¿está seguro?



2 Answers

Como usted señala, cuando múltiples hilos pueden acceder a SomeEvent simultáneamente, un hilo podría verificar si SomeEvent es nulo y determinar que no lo es. Justo después de hacerlo, otro hilo podría eliminar el último delegado registrado de SomeEvent . Cuando el primer subproceso intenta subir SomeEvent , se SomeEvent una excepción. Una forma razonable de evitar este escenario es:

protected virtual void OnSomeEvent(EventArgs args) 
{
    EventHandler ev = SomeEvent;
    if (ev != null) ev(this, args);
}

Esto funciona porque cada vez que se agrega o quita un delegado de un evento utilizando las implementaciones predeterminadas de los accesadores de agregar y eliminar, se utilizan los métodos estáticos Delegate.Combine y Delegate.Remove. Cada uno de estos métodos devuelve una nueva instancia de un delegado, en lugar de modificar la que se le pasó.

Además, la asignación de una referencia de objeto en .NET es atomic , y las implementaciones predeterminadas de los accesos de evento de agregar y quitar están synchronised . Entonces, el código anterior tiene éxito copiando primero el delegado de multidifusión del evento a una variable temporal. Cualquier cambio en SomeEvent después de este punto no afectará la copia que haya realizado y almacenado. Por lo tanto, ahora puede comprobar de forma segura si los delegados se registraron y posteriormente invocarlos.

Tenga en cuenta que esta solución resuelve un problema de carrera, concretamente el de un controlador de eventos que es nulo cuando se invoca. No maneja el problema donde un controlador de eventos está extinto cuando se invoca, o un controlador de eventos se suscribe después de que se toma la copia.

Por ejemplo, si un controlador de eventos depende del estado que se destruye tan pronto como el controlador no está suscrito, esta solución podría invocar un código que no se puede ejecutar correctamente. Vea la excelente entrada del blog de Eric Lippert para más detalles. Además, vea esta pregunta y respuestas de StackOverflow .

EDITAR: Si estás usando C # 6.0, entonces la respuesta de Krzysztof parece una buena manera de hacerlo.

Question

Algo que me confunde, pero que nunca ha causado ningún problema ... la forma recomendada de enviar un evento es la siguiente:

public event EventHandler SomeEvent;
...
{
    ....
    if(SomeEvent!=null)SomeEvent();
}

En un entorno de subprocesos múltiples, ¿cómo garantiza este código que otro subproceso no alterará la lista de invocación de SomeEvent entre la comprobación de nulo y la invocación del evento?




Me gustaría sugerir una ligera mejora en la respuesta de RoadWarrior al utilizar una función de extensión para EventHandler:

public static class Extensions
{
    public static void Raise(this EventHandler e, object sender, EventArgs args = null)
    {
        var e1 = e;

        if (e1 != null)
        {
            if (args == null)
                args = new EventArgs();

            e1(sender, args);
        }                
    }
  }

Con esta extensión en el alcance, los eventos pueden plantearse simplemente por:

clase SomeClass {evento público EventHandler MyEvent;

void SomeFunction()
{
    // code ...

    //---------------------------
    MyEvent.Raise(this);
    //---------------------------
}

}

c# events




La forma recomendada es un poco diferente y utiliza un temporal de la siguiente manera:

EventHandler tmpEvent = SomeEvent;
if (tmpEvent != null)
{
    tmpEvent();
}



Related