2009-07-19 14 views
5

Problema: Tengo una clase de documento que contiene una lista de objetos. Estos objetos generan eventos como SolutionExpired, DisplayExpired etc. El documento debe responder a esto.Eliminando todos los manejadores de eventos de una vez

Los documentos a veces pueden intercambiar objetos, pero un solo objeto nunca debe ser "parte" de más de un documento.

Mi clase de documento contiene un conjunto de métodos que sirven como controladores de eventos. Cada vez que un objeto ingresa al documento, uso AddHandler para configurar los eventos, y cada vez que se quita un objeto del documento, uso RemoveHandler para deshacer el daño. Sin embargo, hay casos en los que es difícil asegurarse de que todos los pasos se tomen correctamente y, por lo tanto, podría terminar con controladores de eventos deshonestos.

Cuento corto; ¿Cómo elimino todos los controladores que apuntan a un método específico? Tenga en cuenta que no tengo una lista de posibles fuentes de eventos, estos podrían almacenarse en cualquier lugar.

Algo así como:

RemoveHandler *.SolutionExpired, AddressOf DefObj_SolutionExpired 
+0

posible duplicado de [Cómo eliminar todos los controladores de eventos de un control] (http://stackoverflow.com/questions/91778/how-to-remove-all-event-handlers-from-a-control) – ChrisF

+0

Posible duplicado de [Cómo eliminar todos los controladores de eventos de un control] (https://stackoverflow.com/questions/91778/how-to-remove-all-event-handlers-from-a-control) –

Respuesta

5

Puede usar Delegate.RemoveAll(). (La parte que le interesa está en button2_Click)

public void Form_Load(object sender, EventArgs e) 
{ 
    button1.Click += new EventHandler(button1_Click); 
    button1.Click += new EventHandler(button1_Click); 
    button2.Click += new EventHandler(button2_Click); 
    TestEvent += new EventHandler(Form_TestEvent); 
} 
event EventHandler TestEvent; 
void OnTestEvent(EventArgs e) 
{ 
    if (TestEvent != null) 
     TestEvent(this, e); 
} 
void Form_TestEvent(object sender, EventArgs e) 
{ 
    MessageBox.Show("TestEvent fired"); 
} 
void button2_Click(object sender, EventArgs e) 
{ 
    Delegate d = TestEvent as Delegate; 
    TestEvent = Delegate.RemoveAll(d, d) as EventHandler; 
} 
void button1_Click(object sender, EventArgs e) 
{ 
    OnTestEvent(EventArgs.Empty); 
} 

Debe tener en cuenta que no altera el contenido de los delegados que se pasa en a ella, devuelve un delegado alterado. En consecuencia, no podrá modificar los eventos en un botón que haya soltado en un formulario del formulario, ya que button1.Click solo puede tener += o -= en él, no =. Esto no va a compilar:

button1.Click = Delegate.RemoveAll(d, d) as EventHandler; 

Además, asegúrese de que donde quiera que estés implementación de este que está mirando hacia fuera para el potencial de las condiciones de carrera.¡Podrías terminar con un comportamiento realmente extraño si eliminas manejadores de un evento que está siendo llamado por otro hilo!

+4

No entiendo cómo esto es aceptable, dada la condición del OP de la necesidad de eliminar manejadores de un evento al que no tiene acceso de nivel privado. ¡Debo estar perdiendo algo! –

+1

También esto es C# mientras que la pregunta de OP era sobre vb.net. Mientras que los dos tienen mucha superposición, la sintaxis del evento es una en la que difieren mucho en – user81993

1
public class TheAnswer 
{ 
    public event EventHandler MyEvent = delegate { }; 

    public void RemoveFromMyEvent(string methodName) 
    { 
     foreach (var handler in MyEvent.GetInvocationList()) 
     { 
      if (handler.Method.Name == methodName) 
      { 
       MyEvent -= (EventHandler)handler; 
      } 
     } 
    } 
} 

EDIT 2: Disculpas por mi incomprensión - Veo que usted era bastante claro por no tener acceso a las fuentes de eventos en su puesto original.

La manera más simple que se me ocurre para resolver este problema consiste en implementar un diccionario compartido de enlaces de objeto a documento. Cuando un objeto ingresa un documento, verifique el diccionario de un enlace existente a otro documento; si está presente, elimine los controladores que hacen referencia al documento anterior antes de agregarlos al nuevo. De cualquier manera, actualice el diccionario con el nuevo enlace.

creo que en la mayoría de los casos, los impactos en el rendimiento y la memoria serían insignificantes: a menos que usted está tratando con muchas decenas de miles de objetos pequeños y con frecuencia intercambiarlos entre documentos, la sobrecarga de la memoria de cada clave/valor par y el impacto en el rendimiento para cada operación de búsqueda debería ser bastante pequeña.

Como alternativa: si puede detectar (en los controladores de eventos del documento) que el remitente del evento ya no es relevante para el documento, puede separar los eventos allí.

Parecen el tipo de ideas que quizás ya has rechazado, ¡pero quizás no!

+0

Ben, gracias por publicar esta. Me temo que no tiene mucho sentido para mí (o para el compilador de VB). ¿Podría publicar el C# original? –

+1

Sabía que debería haber dejado la versión original de C#. :-) –

+0

Gracias Ben, veo lo que estás haciendo aquí, pero si no me equivoco, esta función supone que tengo una referencia a la instancia que está provocando los eventos. Estos objetos podrían haber desaparecido hace tiempo en el horizonte en otra lista en alguna parte. Estoy empezando a pensar que probablemente no sea posible hacerlo ya que los delegados controladores se definen en la clase que genera eventos, no en la clase que define el controlador. –

1

Use Delegate.RemoveAll (tal vez usando la reflexión si la instancia Delegate es privada).

+0

Porque necesito tener una instancia de la fuente para esto. Creo ... Ya no tengo acceso al objeto de evento, solo al manejador. –

Cuestiones relacionadas