¿Ya se ha agregado un controlador de eventos?

183

¿Hay alguna manera de saber si se ha agregado un controlador de eventos a un objeto? Estoy serializando una lista de objetos dentro / fuera del estado de sesión para que podamos usar el estado de sesión basado en SQL ... Cuando un objeto en la lista tiene una propiedad modificada, debe marcarse, lo que el controlador de eventos se ocupó correctamente antes . Sin embargo, ahora cuando los objetos están deserializados, no está obteniendo el controlador de eventos.

En un ataque de molestia leve, acabo de agregar el controlador de eventos a la propiedad Get que accede al objeto. Se llama ahora, lo cual es genial, excepto que se llama como 5 veces, así que creo que el controlador se sigue agregando cada vez que se accede al objeto.

Es realmente lo suficientemente seguro como para ignorarlo, pero prefiero hacerlo mucho más limpio verificando si el controlador ya se ha agregado, por lo que solo lo hago una vez.

¿Es eso posible?

EDITAR: No necesariamente tengo el control total de qué controladores de eventos se agregan, por lo que solo verificar nulo no es lo suficientemente bueno.

CodeRedick
fuente
ver también stackoverflow.com/questions/367523/…
Ian Ringrose

Respuestas:

123

Desde fuera de la clase definitoria, como menciona @Telos, solo puede usar EventHandler en el lado izquierdo de a +=o a -=. Por lo tanto, si tiene la capacidad de modificar la clase definitoria, puede proporcionar un método para realizar la verificación al verificar si el controlador de eventos es null; de ser así, no se ha agregado ningún controlador de eventos. Si no, entonces tal vez y pueda recorrer los valores en Delegate.GetInvocationList . Si uno es igual al delegado que desea agregar como controlador de eventos, entonces sabe que está allí.

public bool IsEventHandlerRegistered(Delegate prospectiveHandler)
{   
    if ( this.EventHandler != null )
    {
        foreach ( Delegate existingHandler in this.EventHandler.GetInvocationList() )
        {
            if ( existingHandler == prospectiveHandler )
            {
                return true;
            }
        }
    }
    return false;
}

Y esto podría modificarse fácilmente para convertirse en "agregar el controlador si no está allí". Si no tiene acceso a las entrañas de la clase que expone el evento, es posible que deba explorar -=y+= , como lo sugiere @Lou Franco.

Sin embargo, es mejor que vuelva a examinar la forma en que está encargando y desmantelando estos objetos, para ver si no puede encontrar una manera de rastrear esta información usted mismo.

Blair Conrad
fuente
77
Esto no se compila, EventHandler solo puede estar en el lado izquierdo de + = o - =.
CodeRedick
2
Se eliminó el voto negativo con más explicaciones. El estado SQL está destruyendo casi toda la idea aquí ... :(
CodeRedick
1
Gracias Blair y SO search, justo lo que estaba buscando (molesto que no puedas hacerlo fuera de la clase)
George Mauer
3
El problema ocurre la mayor parte del tiempo al comparar a los delegados para la igualdad. Así que úselo Delegate.Equals(objA, objB)si desea verificar exactamente la misma existencia de delegado. De lo contrario, compare las propiedades individualmente if(objA.Method.Name == objB.Method.Name && objA.Target.GetType().FullName == objB.Target.GetType().FullName).
Sanjay
2
Este código no funciona en un WinForm. ¿Es estrictamente para ASP.NET?
jp2code
213

Recientemente llegué a una situación similar en la que necesitaba registrar un controlador para un evento solo una vez. Descubrí que puede anular el registro de forma segura primero, y luego registrarse nuevamente, incluso si el controlador no está registrado en absoluto:

myClass.MyEvent -= MyHandler;
myClass.MyEvent += MyHandler;

Tenga en cuenta que hacer esto cada vez que registre su controlador se asegurará de que su controlador se registre solo una vez. Suena como una muy buena práctica para mí :)

alf
fuente
9
Parece arriesgado; Si se dispara un evento después de quitar el controlador y antes de volver a agregarlo, se perderá.
Jimmy
27
Por supuesto. Quieres decir que no es seguro para subprocesos. Pero eso solo puede ser un problema cuando se ejecutan varios subprocesos o similares, lo cual no es habitual. En la mayoría de los casos, esto debería ser lo suficientemente bueno por simplicidad.
alf
77
Esto me molesta El hecho de que actualmente no esté creando explícitamente subprocesos en su código, eso no significa que no haya múltiples subprocesos o que no se agregarán más tarde. Tan pronto como usted (u otra persona en el equipo, posiblemente meses después) agregue un hilo de trabajo o responda tanto a la interfaz de usuario como a la conexión de red, esto abre la puerta a eventos caídos altamente intermitentes.
Technophile
1
Creo que el intervalo de tiempo entre eliminar y agregar nuevamente es tan pequeño que es muy poco probable que existan eventos perdidos, pero sí, aún es posible.
Alisson
2
Si está utilizando esto para algo como actualizar una interfaz de usuario, etc., entonces es una tarea tan trivial que el riesgo está bien. Si esto fuera para el manejo de paquetes de red, etc., entonces no lo usaría.
lanza el
18

Si este es el único controlador, puede verificar si el evento es nulo; de lo contrario, se agregó el controlador.

Creo que puede llamar de forma segura - = en el evento con su controlador incluso si no se agrega (si no, puede atraparlo) - para asegurarse de que no esté allí antes de agregar.

Lou Franco
fuente
3
Esta lógica se romperá tan pronto como el evento se maneje en otro lugar también.
bugged87
6

Este ejemplo muestra cómo usar el método GetInvocationList () para recuperar delegados a todos los controladores que se han agregado. Si está buscando ver si se ha agregado un controlador específico (función), puede usar la matriz.

public class MyClass
{
  event Action MyEvent;
}

...

MyClass myClass = new MyClass();
myClass.MyEvent += SomeFunction;

...

Action[] handlers = myClass.MyEvent.GetInvocationList(); //this will be an array of 1 in this example

Console.WriteLine(handlers[0].Method.Name);//prints the name of the method

Puede examinar varias propiedades en la propiedad Método del delegado para ver si se ha agregado una función específica.

Si está buscando ver si solo hay uno conectado, puede probar nulo.

Jason Jackson
fuente
GetInvocationList () no es miembro de mi clase. De hecho, parece que no puedo encontrar este método en ningún objeto o controlador al que tenga acceso ...
CodeRedick
También lo intenté, aparentemente solo puedes acceder al evento desde dentro de la clase. Estoy haciendo esto genéricamente, y como otros han mencionado, los controladores de eventos probablemente se estén perdiendo de todos modos. ¡Gracias por la aclaración!
CodeRedick
4

Si entiendo su problema correctamente, puede tener problemas más grandes. Dijiste que otros objetos pueden suscribirse a estos eventos. Cuando el objeto se serializa y deserializa, los otros objetos (los que no tiene control) perderán sus controladores de eventos.

Si no está preocupado por eso, mantener una referencia a su controlador de eventos debería ser lo suficientemente bueno. Si le preocupan los efectos secundarios de otros objetos que pierden sus controladores de eventos, entonces puede que desee repensar su estrategia de almacenamiento en caché.

CodeChef
fuente
1
D'oh! Ni siquiera había pensado en eso ... aunque debería haber sido obvio teniendo en cuenta que mi problema original era que mi propio controlador se estaba perdiendo.
CodeRedick
2

Estoy de acuerdo con la respuesta de alf, pero poca modificación es, para usar,

           try
            {
                control_name.Click -= event_Click;
                main_browser.Document.Click += Document_Click;
            }
            catch(Exception exce)
            {
                main_browser.Document.Click += Document_Click;
            }
Desarrollador de software
fuente
2

La única forma en que funcionó para mí es creando una variable booleana que configuré como verdadera cuando agregué el evento. Luego pregunto: si la variable es falsa, agrego el evento.

bool alreadyAdded = false;

Esta variable puede ser global.

if(!alreadyAdded)
{
    myClass.MyEvent += MyHandler;
    alreadyAdded = true;
}
Xtian11
fuente
0
EventHandler.GetInvocationList().Length > 0
benPearce
fuente
2
¿No arroja esto cuando la lista == nulo?
Boris Callens
2
fuera de la clase propietaria del controlador de eventos solo puede usar - = y + =. No puedes acceder al evento.
tbergelt