the example event custom c# garbage-collection event-handling

example - publish event c#



¿Es necesario eliminar explícitamente los controladores de eventos en C# (2)

En tu caso, estás bien. Originalmente leí su pregunta al revés, que un suscriptor salía del alcance, no del editor . Si el editor del evento sale del alcance, las referencias al suscriptor (¡no al suscriptor, por supuesto!) Van con él y no es necesario eliminarlas explícitamente.

Mi respuesta original está debajo, sobre qué sucede si usted crea un suscriptor de evento y lo deja fuera del alcance sin darse de baja. No se aplica a su pregunta, pero la dejo en su lugar para la historia.

Si la clase todavía está registrada a través de controladores de eventos, entonces todavía es alcanzable. Todavía es un objeto en vivo. Un GC que sigue a un gráfico de eventos lo encontrará conectado. Sí, querrá eliminar explícitamente los controladores de eventos.

El hecho de que el objeto esté fuera del alcance de su asignación original no significa que sea un candidato para GC. Mientras permanezca una referencia en vivo, es en vivo.

Tengo una clase que ofrece algunos eventos. Esa clase se declara globalmente pero no se instancia en esa declaración global; se instancia según sea necesario en los métodos que lo necesiten.

Cada vez que se necesita esa clase en un método, se instancia y se registran los controladores de eventos. ¿Es necesario eliminar los controladores de eventos explícitamente antes de que el método salga del alcance?

Cuando el método sale del alcance, también lo hace la instancia de la clase. ¿Dejar controladores de eventos registrados con esa instancia que está fuera del alcance tiene una implicación de huella de memoria? (Me pregunto si el controlador de eventos evita que el GC vea la instancia de la clase como si ya no se hiciera referencia a ella).

Gracias.


En tu caso, todo está bien. El objeto que publica los eventos mantiene vivos los objetivos de los controladores de eventos. Entonces si tengo:

publisher.SomeEvent += target.DoSomething;

entonces el publisher tiene una referencia al target pero no al revés.

En su caso, el editor será elegible para la recolección de basura (suponiendo que no haya otras referencias), por lo que el hecho de que tenga una referencia a los destinos del manejador de eventos es irrelevante.

El caso complicado es cuando el editor tiene una larga vida pero los suscriptores no quieren serlo; en ese caso, debe cancelar la suscripción de los controladores. Por ejemplo, supongamos que tiene algún servicio de transferencia de datos que le permite suscribirse a notificaciones asíncronas sobre los cambios de ancho de banda, y el objeto de servicio de transferencia es de larga duración. Si hacemos esto:

BandwidthUI ui = new BandwidthUI(); transferService.BandwidthChanged += ui.HandleBandwidthChange; // Suppose this blocks until the transfer is complete transferService.Transfer(source, destination); // We now have to unsusbcribe from the event transferService.BandwidthChanged -= ui.HandleBandwidthChange;

(De hecho, desearía utilizar un bloque finally para asegurarse de no filtrar el controlador de eventos). Si no cancelamos la suscripción, BandwidthUI duraría al menos tanto como el servicio de transferencia.

Personalmente, rara vez me encuentro con esto, por lo general, si me suscribo a un evento, el objetivo de ese evento dura al menos tanto como el editor, un formulario durará tanto como el botón que está en él, por ejemplo. Vale la pena saber acerca de este problema potencial, pero creo que algunas personas se preocupan cuando no es necesario, porque no saben en qué dirección se encuentran las referencias.

EDITAR: Esto es para responder el comentario de Jonathan Dickinson. En primer lugar, mira los documentos para Delegate.Equals(object) que claramente dan el comportamiento de igualdad.

En segundo lugar, aquí hay un programa breve pero completo para mostrar el trabajo de desuscripción:

using System; public class Publisher { public event EventHandler Foo; public void RaiseFoo() { Console.WriteLine("Raising Foo"); EventHandler handler = Foo; if (handler != null) { handler(this, EventArgs.Empty); } else { Console.WriteLine("No handlers"); } } } public class Subscriber { public void FooHandler(object sender, EventArgs e) { Console.WriteLine("Subscriber.FooHandler()"); } } public class Test { static void Main() { Publisher publisher = new Publisher(); Subscriber subscriber = new Subscriber(); publisher.Foo += subscriber.FooHandler; publisher.RaiseFoo(); publisher.Foo -= subscriber.FooHandler; publisher.RaiseFoo(); } }

Resultados:

Raising Foo Subscriber.FooHandler() Raising Foo No handlers

(Probado en Mono y .NET 3.5SP1.)

Edición adicional:

Esto es para demostrar que un editor de eventos se puede recopilar mientras todavía hay referencias a un suscriptor.

using System; public class Publisher { ~Publisher() { Console.WriteLine("~Publisher"); Console.WriteLine("Foo==null ? {0}", Foo == null); } public event EventHandler Foo; } public class Subscriber { ~Subscriber() { Console.WriteLine("~Subscriber"); } public void FooHandler(object sender, EventArgs e) {} } public class Test { static void Main() { Publisher publisher = new Publisher(); Subscriber subscriber = new Subscriber(); publisher.Foo += subscriber.FooHandler; Console.WriteLine("No more refs to publisher, " + "but subscriber is alive"); GC.Collect(); GC.WaitForPendingFinalizers(); Console.WriteLine("End of Main method. Subscriber is about to " + "become eligible for collection"); GC.KeepAlive(subscriber); } }

Resultados (en .NET 3.5SP1; Mono parece comportarse de manera extraña aquí. Lo investigaremos un momento):

No more refs to publisher, but subscriber is alive ~Publisher Foo==null ? False End of Main method. Subscriber is about to become eligible for collection ~Subscriber