In Ihrem Fall ist alles in Ordnung. Es ist das Objekt, das die Ereignisse veröffentlicht, das die Ziele der Ereignishandler am Leben erhält . Also wenn ich habe:
publisher.SomeEvent += target.DoSomething;
hat dann publisher
einen Bezug, target
aber nicht umgekehrt.
In Ihrem Fall ist der Herausgeber für die Speicherbereinigung berechtigt (vorausgesetzt, es gibt keine anderen Verweise darauf), sodass die Tatsache, dass er einen Verweis auf die Event-Handler-Ziele enthält, irrelevant ist.
Der schwierige Fall ist, wenn der Publisher langlebig ist, die Abonnenten dies jedoch nicht möchten. In diesem Fall müssen Sie die Handler abbestellen. Angenommen, Sie haben einen Datenübertragungsdienst, mit dem Sie asynchrone Benachrichtigungen über Bandbreitenänderungen abonnieren können, und das Übertragungsdienstobjekt ist langlebig. Wenn wir das tun:
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;
(Sie möchten tatsächlich einen finally-Block verwenden, um sicherzustellen, dass der Ereignishandler nicht durchgesickert ist.) Wenn wir uns nicht abmelden, BandwidthUI
würde der mindestens so lange wie der Übertragungsdienst funktionieren .
Persönlich stoße ich selten darauf - normalerweise, wenn ich ein Ereignis abonniere, lebt das Ziel dieses Ereignisses mindestens so lange wie der Herausgeber - ein Formular hält so lange wie die Schaltfläche, die sich darauf befindet. Es lohnt sich, über dieses potenzielle Problem Bescheid zu wissen, aber ich denke, einige Leute machen sich darüber Sorgen, wenn sie es nicht brauchen, weil sie nicht wissen, in welche Richtung die Referenzen gehen.
EDIT: Dies ist, um Jonathan Dickinsons Kommentar zu beantworten. Schauen Sie sich zunächst die Dokumente für Delegate.Equals (Objekt) an, in denen das Gleichheitsverhalten eindeutig angegeben ist.
Zweitens ist hier ein kurzes, aber vollständiges Programm, das zeigt, wie das Abbestellen funktioniert:
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();
}
}
Ergebnisse:
Raising Foo
Subscriber.FooHandler()
Raising Foo
No handlers
(Getestet auf Mono und .NET 3.5SP1.)
Weiter bearbeiten:
Dies soll beweisen, dass ein Event-Publisher gesammelt werden kann, solange noch Verweise auf einen Abonnenten vorhanden sind.
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);
}
}
Ergebnisse (in .NET 3.5SP1; Mono scheint sich hier etwas seltsam zu verhalten. Wird dies einige Zeit untersuchen):
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