2008-09-30 13 views
125

tomar las siguientes clases C#:¿Cómo puedo borrar las suscripciones de eventos en C#?

c1 { 
event EventHandler someEvent; 
} 

Si hay una gran cantidad de suscripciones a someEvent caso c1 's y quiero aclarar a todos, ¿cuál es la mejor manera de lograr esto? Considere también que las suscripciones a este evento podrían ser delegados lambdas/anónimos.

Actualmente mi solución es añadir un método ResetSubscriptions()-c1 que establece someEvent en nulo. No sé si esto tiene consecuencias no vistas.

Respuesta

164

Desde dentro de la clase, puede establecer la variable (oculta) en nulo. Una referencia nula es la forma canónica de representar efectivamente una lista de invocación vacía.

Desde fuera de la clase, no puede hacer esto: los eventos básicamente exponen "suscribirse" y "cancelar suscripción" y eso es todo.

Vale la pena estar al tanto de lo que realmente hacen los eventos de campo: están creando una variable y un evento al mismo tiempo. Dentro de la clase, terminas haciendo referencia a la variable. Desde afuera, haces referencia al evento.

Consulte mi article on events and delegates para obtener más información.

+9

¡Oh, las maravillas de la programación orientada a objetos. –

+3

Si eres obstinado, puedes forzarlo a despejarlo mediante la reflexión. Consulte http://stackoverflow.com/questions/91778/how-to-remove-all-event-handlers-from-a-control/91853#91853. – Brian

+1

@Brian: depende de la implementación. Si es * solo * un evento parecido a un campo o un 'EventHandlerList', es posible que puedas. Sin embargo, habría que reconocer esos dos casos, y podría haber muchas otras implementaciones. –

28

añadir un método para c1 que establecerá 'algunEvento' a nula ...

class c1 
{ 
    event EventHandler someEvent; 
    ResetSubscriptions() {someEvent = null;} 
} 
+0

¿Estás seguro de que asignar null borrará la lista de invocación? – leppie

+0

Ese es el comportamiento que estoy viendo. Como dije en mi pregunta, no sé si estoy pasando por alto algo. – programmer

5

Esto se puede conseguir mediante el uso de los métodos Delegate.Remove o Delegate.RemoveAll.

+6

No creo que esto funcione con expresiones lambda o delegados anónimos. – programmer

+3

Esta sería una gran sugerencia pero no tiene ejemplos ... –

5

Configurar el evento para anular dentro de la clase funciona. Cuando descarta una clase, siempre debe establecer el evento en nulo, el GC tiene problemas con los eventos y puede no limpiar la clase eliminada si tiene eventos colgantes.

3

Comentario conceptual aburrido extendido.

Prefiero usar la palabra "controlador de eventos" en lugar de "evento" o "delegar". Y usó la palabra "evento" para otras cosas. En algunos lenguajes de programación (VB.NET, Object Pascal, Objective-C), "evento" se denomina "mensaje" o "señal", e incluso tiene una palabra clave "mensaje" y sintaxis específica de azúcar.

const 
    WM_Paint = 998; // <-- "question" can be done by several talkers 
    WM_Clear = 546; 

type 
    MyWindowClass = class(Window) 
    procedure NotEventHandlerMethod_1; 
    procedure NotEventHandlerMethod_17; 

    procedure DoPaintEventHandler; message WM_Paint; // <-- "answer" by this listener 
    procedure DoClearEventHandler; message WM_Clear; 
    end; 

Y, con el fin de responder a ese "mensaje", que responde un "gestor de eventos", si es un solo delegado o varios delegados.

Resumen: "Evento" es la "pregunta", "controlador (es) de eventos" es la (s) respuesta (s).

6

La mejor práctica para borrar a todos los suscriptores es establecer el someEvent como nulo agregando otro método público si desea exponer esta funcionalidad al exterior. Esto no tiene consecuencias no vistas. La condición previa es recordar declarar SomeEvent con la palabra clave 'evento'.

Por favor, vea el libro - C# 4.0 en la cáscara de nuez, página 125.

Alguien aquí propuesto utilizar Delegate.RemoveAll método. Si lo usa, el código de muestra podría seguir el siguiente formulario. Pero es realmente estúpido.¿Por qué no solo SomeEvent=null dentro de la función ClearSubscribers()?

public void ClearSubscribers() 
    { 
      SomeEvent = (EventHandler) Delegate.RemoveAll(SomeEvent, SomeEvent);// Then you will find SomeEvent is set to null. 
    } 
0

Quitar todos los eventos, asumen el evento es de tipo "acción":

Delegate[] dary = TermCheckScore.GetInvocationList(); 

if (dary != null) 
{ 
    foreach (Delegate del in dary) 
    { 
     TermCheckScore -= (Action) del; 
    } 
} 
+0

Si está dentro del tipo que declaró el evento, no necesita hacer esto, puede establecerlo en nulo, si está fuera del tipo, entonces no puede obtener la lista de invocación del delegado. . Además, su código arroja una excepción si el evento es nulo, al llamar a 'GetInvocationList'. – Servy

6
class c1 
{ 
    event EventHandler someEvent; 
    ResetSubscriptions() {someEvent = delegate{};} 
} 

Es mejor utilizar delegado {} de nulo

1

Ésta es mi solución:

public class Foo : IDisposable 
{ 
    private event EventHandler _statusChanged; 
    public event EventHandler StatusChanged 
    { 
     add 
     { 
      _statusChanged += value; 
     } 
     remove 
     { 
      _statusChanged -= value; 
     } 
    } 

    public void Dispose() 
    { 
     _statusChanged = null; 
    } 
} 

Necesita llamar al Dispose() o use using(new Foo()){/*...*/} patrón para anular la suscripción de todos los miembros de la lista de invocación.

Cuestiones relacionadas