2011-03-04 7 views
5

Si creo una clase .NET, que se suscribe a un evento con una función anónima como esto:¿Debo eliminar este tipo de controlador de eventos?

void MyMethod() 
{ 
    Application.Current.Deactivated += (s,e) => { ChangeAppearance(); }; 
} 

Será este controlador de eventos que la raíz mantener mi clase de ser recogido de basura?

Si no, ¡wohoo! Pero si es así, ¿puedes mostrarme la sintaxis de eliminación? Solo usar - = con el mismo código parece incorrecto.

Respuesta

3

También se puede usar un método "real" como Simon D. sugiere, o hacer esto:

EventHandler handler = null; 
handler = (s,e) => { 
    Application.Current.Deactivated -= handler; 
    ChangeAppearance(); 
}; 
Application.Current.Deactivated += handler; 

Dado que este es un poco feo y en contra del propósito de utilizar un lambda ser sucinta, I 'd probablemente vaya con la refactorización a un método. Pero es útil saber qué más funciona.

Advertencia: No hace falta decir que usted tiene que ser super ultra-cuidado de no meterse con el valor de handler en absoluto entre el punto en el que se suscribe al evento y el punto en el que se invoca realmente, de lo contrario el la suscripción a la suscripción no funcionará correctamente.

+0

Me gusta el estilo succint aquí. Para mis necesidades, esto debería funcionar perfectamente. Gracias por tomarse el tiempo para escribir esto! – jschroedl

3

Creo que es necesario darse de baja, porque el proveedor del evento (la aplicación) vive, o al menos puede vivir, más que su consumidor. Entonces, cada instancia de suscripción que muere mientras la aplicación sigue viva generará pérdidas de memoria.

Usted se está suscribiendo a un delegado anónimo al evento. Es por eso que no puede darse de baja de la misma manera porque ya no puede abordarlo. Actualmente está creando el método en el mismo momento en que se suscribe, y no almacena ningún puntero al método recién creado.

Si cambia ligeramente su aplicación para utilizar un método "real", puede darse de baja fácilmente desde el evento de la misma manera se suscribe a ella:

Application.Current.Deactivated += ChangeAppearance; 
Application.Current.Deactivated -= ChangeAppearance; 
private void ChangeAppearance(object sender, EventArgs eventArgs) 
{ 
    throw new NotImplementedException(); 
} 
+0

Gracias por confirmar la sospecha. -John – jschroedl

1

Este es de hecho una fuga que impide la recolección de basura. Hay formas de evitarlo, con WeakReference siendo uno de los mejores.

This link tiene una buena conversación y buenas respuestas para usted.

+0

Gracias por el puntero. ¡Esos chicos se ponen bonitos en esto! – jschroedl

2

Definitivamente tiene que limpiar la referencia. Si hubiera alguna duda, podría probar fácilmente con su propio evento estático como ese.

static class MemoryLeak 
{ 
    static List<Action<int>> list = new List<Action<int>>(); 
    public static event Action<int> ActivateLeak 
    { 
     add 
     { 
      list.Add(value); 
     } 
     remove 
     { 
      list.Remove(value); 
     } 
    } 
} 

Luego, al establecer un punto de interrupción en la función de eliminación, puede ver que su referencia no está limpia.

class Program 
{ 
    static void Main(string[] args) 
    { 
     foo f = new foo(); 
     MemoryLeak.ActivateLeak += o => f.bar(); 
     f.tryCleanup(); 
    } 
} 

class foo 
{ 
    public void bar() 
    { } 

    public void tryCleanup() 
    { 
     MemoryLeak.ActivateLeak -= o => bar(); 
    } 
} 

Como alternativa a la solución de Simon se puede utilizar un segundo cierre para crear una acción de "separar", que se puede pasar alrededor.

foo f = new foo(); 
Action<int> callfoo = o => f.bar(); 
MemoryLeak.ActivateLeak += callfoo; 
Action cleanUp =() => MemoryLeak.ActivateLeak -= callfoo; 

// Now you can pass around the cleanUp action and call it when you need to unsubscribe from the event. 
cleanUp(); 
Cuestiones relacionadas