2010-01-05 7 views
14

Permite decir que tenemos 2 objetos, locutor y oyente. El locutor tiene un evento llamado Difusión al que está suscrito el Oyente. Si el Listener se elimina sin darse de baja del evento Broadcast, se mantendrá en la memoria debido al delegado del evento que lo referencia que Broadcaster contiene.¿Los eventos personalizados deben establecerse como nulos al desechar un objeto?

Lo que me llama la atención es si la emisora ​​está dispuesta sin la cancelación de la suscripción del oyente o la configuración de la emisora ​​Broadcast = null ¿Se retendrá la emisora ​​en la memoria?

No he podido encontrar nada con una respuesta difícil a esta pregunta excepto un blogger que cree que no establecer el evento como nulo mantendrá la fuente en la memoria (encontrado here).

Me gustaría escuchar una explicación de por qué o por qué no.

Gracias.

ACTUALIZACIÓN: Forum Thread where one developer indicates events should be set to null, but Jon Skeet indicates it's not necessary, but doesn't elaborate.

+0

Supongo que quiere decir "¿se retendrá el Oyente en la memoria?" –

+0

Yo no. Me refiero a Broadcaster. La pregunta es sobre una fuente de evento que se eliminará mientras el suscriptor aún esté vivo. –

+0

(respondió al nuevo comentario) –

Respuesta

9

Tenga en cuenta que los delegados no se mantienen con vida el editor (que sólo mantienen el target = suscriptor vivos), así que no hay número de suscripciones (por sí mismos) mantener la emisora ​​vivo. Como tal, desde esta perspectiva, no importa si está dispuesto o no. Cuando no hay elementos que hagan referencia a la emisora ​​(y las suscripciones a eventos no importan), será elegible para la recopilación.

Esencialmente, un delegado es un (lista de) par (s) de MethodInfo y object referencias; el método para llamar y el objeto para invocar como "arg0" (también conocido como this). Simplemente no tiene una referencia al objeto elevando el evento.

Aquí hay evidencia de que un oyente no mantiene viva la fuente; debería ver que se obtiene la "Fuente 1", a pesar de que todavía tenemos el oyente coincidente que está suscrito. Como era de esperar, "Listener 2" hace no consiguen recogidos, ya que todavía tenemos la emisora ​​juego:

class DataSource 
{ 
    public DataSource(string name) { this.name = name; } 
    private readonly string name; 
    ~DataSource() { Console.WriteLine("Collected: " + name); } 

    public event EventHandler SomeEvent; 
} 
class DataListener 
{ 
    public DataListener(string name) { this.name = name; } 
    private readonly string name; 
    ~DataListener() { Console.WriteLine("Collected: " + name); } 
    public void Subscribe(DataSource source) 
    { 
     source.SomeEvent += SomeMethodOnThisObject; 
    } 
    private void SomeMethodOnThisObject(object sender, EventArgs args) { } 
} 

static class Program 
{ 
    static void Main() 
    { 
     DataSource source1 = new DataSource("Source 1"), 
       source2 = new DataSource("Source 2"); 
     DataListener listener1 = new DataListener("Listener 1"), 
       listener2 = new DataListener("Listener 2"); 
     listener1.Subscribe(source1); 
     listener2.Subscribe(source2); 
     // now we'll release one source and one listener, and force a collect 
     source1 = null; 
     listener2 = null; 
     GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); 
     GC.WaitForPendingFinalizers(); // source 1 gets collected, ONLY 

     Console.WriteLine("Done"); 
     Console.ReadLine(); 
     GC.KeepAlive(source2); // prevents collection due to optimisation 
     GC.KeepAlive(listener1); // prevents collection due to optimisation 
    } 
} 
+0

Sí, pero podría plantear la respuesta de que MethodInfo hace referencia al método de llamada que está en el suscriptor y, por lo tanto, crea una referencia entre el emisor y el suscriptor. La explicación podría estar en la dirección de la referencia (Emisor -> Suscriptor, no al revés). –

+0

@Dan - Absolutamente, ** está ** en la dirección del emisor al suscriptor, que es exactamente por qué la referencia no mantiene viva a la emisora. –

+0

En casos simples, no importará si un editor de eventos anula su lista de suscriptores en 'Dispose'. Por otro lado, si algo mantiene una referencia rooteada inútil a un editor de eventos después de que se elimine, cualquier lista de suscriptores que no se anule se convertirá en referencias rooteadas inútiles para esos suscriptores. Nuking listas de suscriptores en disposición pueden convertir lo que sería grandes pérdidas de memoria en pequeños. A veces eso puede ser algo bueno (si se convierte en una pérdida de memoria que matará a un programa después de 45 minutos en uno que lo mata después de 45 días, y el programa solo necesita ejecutarse durante 60 minutos). – supercat

4

No. El objetivo de delegado en el evento de difusión se hace referencia al objeto de escucha. Eso mantendrá vivo el objeto Listener. El objeto Listener no tiene ninguna referencia de regreso al objeto Broadcast.

Ten cuidado con la terminología. La eliminación del objeto Broadcast no hace nada. Tiene que ser basura recolectada, lo cual solo puede suceder cuando no quedan referencias al objeto. Cuando eso suceda, el objeto delegado también se recopilará automáticamente, ya que la única referencia a él es la lista interna de destinos delegados mantenida por un objeto delegado de evento privado. Eso también elimina la referencia que el delegado tiene para el oyente. Si no hay otras referencias al oyente, se recopilará también. Si aún lo es, ya no recibirá notificaciones de eventos. Para resumir: no tiene que establecer explícitamente el evento como nulo en la clase Broadcast.

No es exactamente lo mismo en el oyente, se hace referencia por el evento al que está suscrito. Si se declara no apto para el negocio (desechado) pero la emisora ​​aún está en vivo, debe eliminar su suscripción al evento de manera explícita. La clase SystemEvents es una versión extrema de eso, sus eventos son estáticos. La activación de eventos en un delegado que hace referencia a un oyente dispuesto es algo que tiende a notar.

La mayoría de los modelos de objetos prácticos intentan garantizar que los objetos del oyente desaparezcan cuando el padre va. Windows Forms sería un buen ejemplo. No es necesario anular la suscripción de eventos explícitamente.

+0

En realidad, puede establecer un evento como nulo desde dentro de la clase que lo declara. –

+0

¿Qué quiere decir con "¿Qué es algo que la clase Broadcast no puede hacer, no puede generar una instancia de delegado para anular la suscripción"? –

+0

Tienes razón, modifiqué la publicación. –

Cuestiones relacionadas