2009-07-02 13 views
21

Si mi software tiene dos instancias de objeto, una de las cuales está suscrita a los eventos de la otra. ¿Debo cancelar la suscripción entre ellos antes de que sean huérfanos para que el recolector de basura los limpie? ¿O hay alguna otra razón por la que debería borrar las relaciones del evento? ¿Qué pasa si el suscrito al objeto es huérfano pero el suscriptor no lo es, o viceversa?¿Debo eliminar las suscripciones a eventos de los objetos antes de que queden huérfanos?

Respuesta

22

Sí, lo haces. Los editores de eventos tienen referencias a los objetos y evitarían que sean recolectados.

Veamos un ejemplo para ver qué sucede. Tenemos dos clases; uno expone un evento, el otro lo consume:

class ClassA 
{ 
    public event EventHandler Test; 
    ~ClassA() 
    { 
     Console.WriteLine("A being collected"); 
    } 
} 
class ClassB 
{ 
    public ClassB(ClassA instance) 
    { 
     instance.Test += new EventHandler(instance_Test); 
    } 

    ~ClassB() 
    { 
     Console.WriteLine("B being collected"); 
    } 

    void instance_Test(object sender, EventArgs e) 
    { 
     // this space is intentionally left blank 
    } 
} 

Nota cómo ClassB no almacena una referencia a la instancia claseA; simplemente conecta un controlador de eventos.

Ahora, veamos cómo se recogen los objetos. Escenario 1:

ClassB temp = new ClassB(new ClassA()); 
Console.WriteLine("Collect 1"); 
GC.Collect(); 
Console.ReadKey(); 
temp = null; 
Console.WriteLine("Collect 2"); 
GC.Collect(); 
Console.ReadKey(); 

Creamos una instancia de ClassB y la mantenemos como referencia a través de la variable temp. Se pasa una nueva instancia de ClassA, donde no almacenamos ninguna referencia en ninguna parte, por lo que queda fuera del alcance inmediatamente después de que el constructor de ClassB finaliza. Hacemos que el recolector de basura se ejecute una vez cuando ClassA ha salido del alcance, y una vez cuando ClassB está fuera de alcance. La salida:

Collect 1 
A being collected 
Collect 2 
B being collected 

Escenario 2:

ClassA temp = new ClassA(); 
ClassB temp2 = new ClassB(temp); 
temp2 = null; 
Console.WriteLine("Collect 1"); 
GC.Collect(); 
Console.ReadKey(); 
temp = null; 
Console.WriteLine("Collect 2"); 
GC.Collect(); 
Console.ReadKey(); 

se crea una nueva instancia de ClassA y una referencia a ella se almacena en la variable TEMP. Luego se crea una nueva instancia de ClassB, obteniendo la instancia de ClassA en la temperatura que se le pasa, y almacenamos una referencia en temp2. Luego configuramos temp2 para que anule, haciendo que la instancia de ClassB salga del alcance. Como antes, tenemos el recolector de basura ejecutado después de que cada instancia haya salido del alcance. La salida:

Collect 1 
Collect 2 
B being collected 
A being collected 

Entonces, para concluir; si la instancia que expone un evento queda fuera del alcance, queda disponible para la recolección de elementos no utilizados, independientemente de si hay controladores de eventos conectados o no. Si una instancia que tiene un controlador de eventos conectado a un evento en otra instancia, no estará disponible para la recolección de elementos no utilizados hasta que el controlador de eventos se separe o la instancia a la que está conectado el controlador de eventos esté disponible para la recolección de elementos no utilizados.

9

Sólo es necesario desenganchar eventos si el objeto exponer los eventos es de larga duración, pero el objeto de enganche el caso de otro modo sería de corta duración (y obtener el recolector de basura con bastante rapidez).

En este caso, el hecho de que no se desenganche causará lo que equivale a una pérdida de memoria, porque su objeto efímero no podrá ser GCed, ya que el evento en el objeto de larga duración tiene un delegado, que contiene una referencia al objeto efímero. Como ese delegado aún hace referencia al objeto efímero, no se puede recolectar basura.

Los eventos estáticos duran por definición, viven hasta que finaliza el programa. Si conecta un evento estático, definitivamente debe desengancharlo cuando haya terminado.

Si ambos objetos están a punto de quedar huérfanos, no es necesario desengancharlos.

5

La suscripción a un evento da como resultado una fuerte referencia al suscriptor. Esto se debe a que, bajo las coberturas, los eventos son delegados, y los delegados a los métodos de instancia son una combinación de la referencia del objeto y el método real. Si no se da de baja, el editor continuará manteniendo las referencias, y los objetos suscritos nunca se volverán verdaderamente huérfanos (y GC) siempre que el editor esté activo.

Lo contrario no es cierto, es decir, el objeto suscrito no tiene ninguna referencia al editor.

Cuestiones relacionadas