W twoim przypadku wszystko jest w porządku. Jest to obiekt, który publikuje zdarzenia, który utrzymuje cele programów obsługi zdarzeń na żywo. Więc jeśli mam:
publisher.SomeEvent += target.DoSomething;
to publisher
ma odniesienie dotarget
ale nie odwrotnie.
W twoim przypadku wydawca będzie kwalifikował się do wyrzucania elementów bezużytecznych (zakładając, że nie ma do niego innych odniesień), więc fakt, że ma odniesienie do celów obsługi zdarzeń, jest nieistotny.
Trudny przypadek polega na tym, że wydawca jest długowieczny, ale subskrybenci nie chcą - w takim przypadku musisz zrezygnować z subskrypcji programów obsługi. Na przykład załóżmy, że masz usługę przesyłania danych, która umożliwia subskrybowanie asynchronicznych powiadomień o zmianach przepustowości, a obiekt usługi transferu jest długotrwały. Jeśli to zrobimy:
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;
(Właściwie chciałbyś użyć bloku last, aby upewnić się, że nie wyciekniesz z modułu obsługi zdarzeń.) Jeśli nie anulowaliśmy subskrypcji, to BandwidthUI
działałby przynajmniej tak długo, jak usługa transferu.
Osobiście rzadko się z tym spotykam - zazwyczaj jeśli zapiszę się na wydarzenie, cel tego wydarzenia żyje co najmniej tak długo, jak wydawca - formularz będzie trwał tak długo, jak na przykład przycisk, który na nim jest. Warto wiedzieć o tym potencjalnym problemie, ale myślę, że niektórzy martwią się tym, kiedy nie potrzebują, ponieważ nie wiedzą, w którą stronę prowadzą odniesienia.
EDYCJA: To jest odpowiedź na komentarz Jonathana Dickinsona. Po pierwsze, spójrz na dokumentację Delegate.Equals (obiekt) która jasno przedstawia zachowanie równości.
Po drugie, oto krótki, ale kompletny program pokazujący działanie anulowania subskrypcji:
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();
}
}
Wyniki:
Raising Foo
Subscriber.FooHandler()
Raising Foo
No handlers
(Testowane na Mono i .NET 3.5SP1.)
Dalsza edycja:
Ma to na celu udowodnienie, że wydawcę wydarzeń można zebrać, dopóki istnieją odniesienia do subskrybenta.
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);
}
}
Wyniki (w .NET 3.5SP1; Mono wydaje się tutaj zachowywać nieco dziwnie. Przyjrzymy się temu kiedyś):
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