memory leaks - leaks - ¿El uso de lambdas como controladores de eventos causa una pérdida de memoria?
memory leak juego (5)
Digamos que tenemos el siguiente método:
private MyObject foo = new MyObject();
// and later in the class
public void PotentialMemoryLeaker(){
int firedCount = 0;
foo.AnEvent += (o,e) => { firedCount++;Console.Write(firedCount);};
foo.MethodThatFiresAnEvent();
}
Si se crea una instancia de la clase con este método y se llama al método PotentialMemoryLeaker
varias veces, ¿se pierde la memoria?
¿Hay alguna forma de desenganchar ese controlador de eventos lambda después de que hayamos terminado de llamar a MethodThatFiresAnEvent
?
Bueno, puede ampliar lo que se ha hecho aquí para hacer que los delegados sean más seguros de usar (sin pérdida de memoria)
No solo perderás memoria, también recibirás tu lambda varias veces. Cada llamada de ''PotentialMemoryLeaker'' agregará otra copia de la lambda a la lista de eventos, y se llamará a cada copia cuando se active ''AnEvent''.
Sí, de la misma manera que los controladores de eventos normales pueden causar fugas. Porque la lambda en realidad se cambia a:
someobject.SomeEvent += () => ...;
someobject.SomeEvent += delegate () {
...
};
// unhook
Action del = () => ...;
someobject.SomeEvent += del;
someobject.SomeEvent -= del;
Entonces, básicamente, es poca mano para lo que hemos estado usando en 2.0 todos estos años.
Sí, guárdelo en una variable y desengánchelo.
DelegateType evt = (o, e) => { firedCount++; Console.Write(firedCount); };
foo.AnEvent += evt;
foo.MethodThatFiresAnEvent();
foo.AnEvent -= evt;
Y sí, si no lo hace, perderá memoria, ya que conectará un nuevo objeto delegado cada vez. También te darás cuenta de esto porque cada vez que llamas a este método, irá a la consola un número creciente de líneas (no solo un número creciente, sino que por una llamada a MethodThatFiresAnEvent arrojará cualquier cantidad de elementos, una vez por cada método anónimo conectado).
Su ejemplo solo se compila en una clase interna privada con el nombre de compilador (con field firedCount y un método con nombre de compilador). Cada llamada a PotentialMemoryLeaker crea una nueva instancia de la clase de cierre a la que foo guarda una referencia por medio de un delegado al único método.
Si no hace referencia al objeto completo que posee PotentialMemoryLeaker, entonces todo será basura. De lo contrario, puede configurar foo para anular o vaciar la lista de controladores de eventos de foo escribiendo esto:
foreach (var handler in AnEvent.GetInvocationList()) AnEvent -= handler;
Por supuesto, necesitarás acceso a los miembros privados de la clase MyObject .