Nel tuo caso, va tutto bene. È l'oggetto che pubblica gli eventi che mantiene in vita gli obiettivi dei gestori di eventi. Quindi se ho:
publisher.SomeEvent += target.DoSomething;
quindi publisher
ha un riferimento a target
ma non viceversa.
Nel tuo caso, l'editore sarà idoneo per la garbage collection (supponendo che non ci siano altri riferimenti ad esso), quindi il fatto che abbia un riferimento alle destinazioni del gestore di eventi è irrilevante.
Il caso complicato è quando l'editore è longevo ma gli abbonati non vogliono esserlo: in tal caso è necessario annullare l'iscrizione ai gestori. Ad esempio, supponi di avere un servizio di trasferimento dati che ti consente di iscriverti a notifiche asincrone sulle modifiche della larghezza di banda e che l'oggetto del servizio di trasferimento sia di lunga durata. Se lo facciamo:
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;
(In realtà vorresti usare un blocco finalmente per assicurarti di non perdere il gestore di eventi.) Se non cancellassimo l'iscrizione, allora BandwidthUI
vivrebbe almeno quanto il servizio di trasferimento.
Personalmente mi imbatto raramente in questo - di solito se mi iscrivo a un evento, il target di quell'evento vive almeno quanto l'editore - un modulo durerà quanto il pulsante che si trova su di esso, per esempio. Vale la pena conoscere questo potenziale problema, ma penso che alcune persone se ne preoccupano quando non ne hanno bisogno, perché non sanno da che parte vanno i riferimenti.
EDIT: Questo è per rispondere al commento di Jonathan Dickinson. In primo luogo, guarda i documenti per Delegate.Equals (oggetto) che danno chiaramente il comportamento di uguaglianza.
In secondo luogo, ecco un programma breve ma completo per mostrare il funzionamento dell'annullamento dell'iscrizione:
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();
}
}
risultati:
Raising Foo
Subscriber.FooHandler()
Raising Foo
No handlers
(Testato su Mono e .NET 3.5SP1.)
Ulteriore modifica:
Questo per dimostrare che un editore di eventi può essere raccolto mentre ci sono ancora riferimenti a un abbonato.
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);
}
}
Risultati (in .NET 3.5SP1; Mono sembra comportarsi in modo leggermente strano qui. Lo esamineremo un po 'di tempo):
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