En tu caso, todo está bien. Es el objeto que publica los eventos que mantiene activos los objetivos de los controladores de eventos. Entonces, si tengo:
publisher.SomeEvent += target.DoSomething;
luego publisher
tiene una referencia a, 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 a él), por lo que el hecho de que tenga una referencia a los destinos del controlador 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, suponga que tiene algún servicio de transferencia de datos que le permite suscribirse a notificaciones asincrónicas sobre 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;
(En realidad, querría usar un bloque finalmente para asegurarse de no filtrar el controlador de eventos). Si no cancelamos la suscripción, entonces el servicio BandwidthUI
permanecería al menos tanto tiempo 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 vive al menos tanto como el editor, un formulario durará tanto como el botón que está en él, por ejemplo. Vale la pena conocer este problema potencial, pero creo que algunas personas se preocupan por él cuando no es necesario, porque no saben hacia dónde van las referencias.
EDITAR: Esto es para responder al comentario de Jonathan Dickinson. En primer lugar, mire los documentos de Delegate.Equals (objeto) que claramente dan el comportamiento de igualdad.
En segundo lugar, aquí hay un programa breve pero completo para mostrar que la cancelación de la suscripción funciona:
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 se puede recopilar un editor de eventos 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 un poco extraña aquí. Lo veremos en algún 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