2009-04-21 15 views
16

No estoy seguro si tengo completamente claro las implicaciones de adjuntar eventos a objetos.¿En qué casos es necesario desapegarse de los eventos?

Ésta es mi comprensión actual, correcta o elaborado:

1. Colocación de eventos de clase locales no tienen que ser separado

Ejemplos:

this.Closing += new System.ComponentModel.CancelEventHandler(MainWindow_Closing);

public event EventHandler OnMyCustomEvent = delegate { };

Supongo que cuando su ob ject se elimina o se recolecta basura, las funciones se desasignan y se separarían automáticamente de los eventos.

2. Colocación de objetos que ya no necesita (= null;) tienen que ser separado de

Ejemplos: Colocación en caso de transcurrido un temporizador, que sólo responde a una vez. Asumiría que necesita almacenar el temporizador en una variable local para poder separar el evento transcurrido después de que se desate el evento. Por lo tanto, se declara el temporizador en un ámbito método local al igual que daría lugar a una fuga:

System.Timers.Timer myDataTimer = new System.Timers.Timer(1000); myDataTimer.Elapsed += new System.Timers.ElapsedEventHandler(myDataTimer_Elapsed);

3. Colocación de eventos en un objeto local a su clase no requiere la eliminación?

Por ejemplo, si tiene un ObservableCollection que crea, supervisa y deja morir. Si adjuntó al evento CollectionChanged utilizando una función privada local, ¿esta función no se desasignaría cuando su clase se recolectara como basura, lo que ocasionaría que también se liberara la ObservableCollection?

Estoy seguro de que tengo lugares donde he dejado de usar objetos y no he podido separarme de un evento (por ejemplo, el ejemplo del temporizador que hice), entonces estoy buscando una explicación más clara de cómo funciona .

Respuesta

22

Creo que lo está haciendo más complicado de lo necesario. Solo necesita recordar dos cosas:

  • Cuando se suscribe a un evento, el "propietario" del evento (el editor) generalmente mantiene una referencia al delegado con el que se suscribe.
  • Si utiliza un método de instancia como la acción de un delegado, entonces el delegado tiene una referencia a su objeto "objetivo".

Esto significa que si usted escribe:

publisher.SomeEvent += subscriber.SomeMethod; 

Entonces subscriber NO serán elegibles para la recolección de basura antes de publisher es a menos que se da de baja después.

Tenga en cuenta que en muchos casos, es sólo subscriberthis:

publisher.SomeEvent += myDataTimer_Elapsed; 

es equivalente a:

publisher.SomeEvent += this.myDataTimer_Elapsed; 

asumiendo que es un método de instancia.

No hay no relación inversa debido a la suscripción al evento - en otras palabras, el suscriptor no mantiene vivo al editor.

Para más información, por favor consulte my article on events and delegates.

+0

Tienes razón, estoy complicando las cosas. Al buscar ejemplos, casi todos mostraron desapego del evento, lo que solo me llevó a creer que el suscriptor podría mantener vivo al editor. –

+0

Pregunta estúpida: ¿Eso implica que si el editor se suscribe a un evento en el suscriptor, ninguno de los dos puede/será recolectado? –

+2

No, significa que hay una referencia circular: tan pronto como no haya * otras * referencias rooteadas a ninguna de ellas, ambas serán elegibles para GC. –

1

El caso relevante donde usted tiene que darse de baja de un evento es la siguiente:

public class A 
{ 
    // ... 
    public event EventHandler SomethingHappened; 
} 

public class B 
{ 
    private void DoSomething() { /* ... */ } // instance method 

    private void Attach(A obj) 
    { 
     obj.SomethingHappened += DoSomething(); 
    } 
} 

En este escenario, cuando se deshaga de un B, todavía habrá una referencia colgando a ella desde obj 's controlador de eventos. Si desea reclamar la memoria de B, primero debe separar B.DoSomething() del controlador de eventos relevante.

Usted podría funcionar en el mismo si la línea de suscripción de eventos se parecía a esto, por supuesto:

obj.SomethingHappened += someOtherObject.Whatever.DoSomething(); 

Ahora es someOtherObject que está en el gancho y no puede ser basura recogida.

3

Las referencias restantes que impiden la recolección de basura tienen un efecto más que puede ser obvio, pero que no se ha mencionado aún en este hilo; el controlador de eventos adjunto también se eliminará.

Lo he experimentado un par de veces. Una era cuando teníamos una aplicación que gradualmente se volvía más lenta y lenta cuanto más tiempo corría. La aplicación creó la interfaz de usuario de forma dinámica cargando controles de usuario. El contenedor hizo que los controles de usuario se suscribieran a ciertos eventos en el entorno, y uno de ellos no se anuló cuando los controles se "descargaron".

Después de un tiempo esto llevó a que se ejecutara un gran número de escuchas de eventos cada vez que se producía un evento en particular. Por supuesto, esto puede conducir a condiciones serias de carrera cuando un buen número de instancias "dormidas" se despiertan repentinamente e intentan actuar con la misma entrada.

En resumen; si escribe código para conectar un detector de eventos; asegúrese de liberarlo tan pronto como ya no lo necesite. Casi me atrevo a prometer que lo salvará de al menos un dolor de cabeza en algún momento en el futuro.

Cuestiones relacionadas