2009-06-05 29 views
7

Digamos que tengo una clase A, que puede disparar un evento llamado X. Ahora tengo una clase B y en un método consigo una instancia de A y ato el caso de un controlador en B:¿Cómo deshacerse de los controladores de eventos de forma segura?

public void BindEvent(A a) 
{ 
    a.X += AEventHandler; 
} 

Tengo tres preguntas sobre esto.

  • ¿Es cierto que cuando ahora me puse la referencia a la instancia B en null, no será recolectado desde el recolector de basura piensa que todavía está en uso (manteniendo así un inútil y potencialmente interferir copia de B en la memoria).

  • ¿Qué sucede cuando tengo otro objeto c (de clase C) en el que tengo una referencia a A llamado a ("this.a = new A()"). Luego llamo "b.BindEvent (this.a)", y en c establezco la referencia a a a null ("this.a = null"). ¿Esto mantendrá la copia de A en la memoria porque se hace referencia a través del evento en b?

  • Si alguno o ambos son ciertos de lo anterior, ¿cómo puedo evitar estos problemas? Si tengo una lista completa de manejadores de eventos (digamos 10 líneas como "a.SomeEvent + = SomeMethod") debería limpiarlos de nuevo ("a.SomeEvent - = SomeMethod"). ¿En qué momento o lugar en el código debería hacer estas cosas?

Bueno, se ha vuelto un poco borroso, pero no estoy seguro de cómo explicarlo de una mejor manera. Deje un comentario si necesito explicar algo más detallado.

Respuesta

8

entonces: A es el editor y B es el suscriptor?

primera bala: si B es la instancia con AEventHandler - entonces es todavía en uso, así que no, que no conseguirá recogido a menos que el ejemplo a es inalcanzable.

segunda viñeta: ¿eh? (se leerá nuevamente ...) Si las instancias A y B son inalcanzables, serán basura recolectada; el evento no importa Si A es alcanzable, entonces B se mantendrá con vida. Sin embargo, la suscripción al evento nunca mantiene A vivo; es una forma ... A puede mantener vivo B, pero B no mantiene vivo A. ¿Eso lo cubre?

tercera viñeta: en la mayoría de los casos, las dos cosas tienen una vida útil similar, por lo que no es un problema. Solo se convierte en un problema si la cosa que publica el evento vive mucho más que las cosas con los controladores. En ese caso, solo tiene que limpiar religiosamente después de usted, por ejemplo: a.X -= AEventHandler. En particular, static eventos son evil por este motivo.

4

Debería desvincular realmente el controlador de eventos antes de destruir la instancia de clase con la que se relaciona. (Uso de su código como un exmaple.)

public void UnbindEvent(A a) 
{ 
    a.X -= AEventHandler; 
} 

También me gustaría preguntar, ¿por qué estás configurando las variables de clase en nulo?

2
  1. Correcto.
  2. Correcto.
  3. ¿Por qué querría evitar este comportamiento? Así es como el GC está diseñado para funcionar. Si necesita hacer una limpieza cuando cada objeto se derriba, use el patrón Dispose.
+0

Estoy bastante seguro de que 2 es incorrecto. En primer lugar, el evento * never * mantiene vivo al editor ('A') y, en segundo lugar, nada puede ver' A' de todos modos ... por lo que los datos se desconectan y se recopilan. –

+0

Doh! Estoy corregido. :-) –

1

Sí, el evento es referencias, si no cancela el registro, el objeto que implementa el controlador de eventos no será basura.

Usted puede hacer esto:

quita todos los eventos registrados, si A sabe cuando no se utilizan más.

class A 
{ 
    // clearing all registrations 
    private void ClearEvents() 
    { 
    X = null; 
    } 
} 

O usted anula el registro en B, si B sabe cuando ya no lo usa. Debe mantener una referencia a a para poder anular el registro.

También podría implementar IDisposable.

class B : IDisposable 
{ 
    private A registeredToA; 

    public void BindEvent(A a) 
    { 
    registeredToA = a; 
    registeredToA.X += AEventHandler; 
    } 

    public void Dispose() 
    { 
    registeredToA.x -= AEventHandler; 
    } 
} 

Este es un cambio radical en su código, porque B siempre tiene que desecharse.

Cuestiones relacionadas