В твоем случае все нормально. Это объект, который публикует события, который поддерживает цели обработчиков событий. Итак, если у меня есть:
publisher.SomeEvent += target.DoSomething;
then publisher
имеет ссылку, target
но не наоборот.
В вашем случае издатель будет иметь право на сборку мусора (при условии, что на него нет других ссылок), поэтому тот факт, что он получил ссылку на цели обработчика событий, не имеет значения.
Сложный случай - это когда издатель долгожитель, а подписчики не хотят им быть - в этом случае вам нужно отписаться от обработчиков. Например, предположим, что у вас есть служба передачи данных, которая позволяет подписаться на асинхронные уведомления об изменениях пропускной способности, а объект службы передачи является долгоживущим. Если мы сделаем это:
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;
(На самом деле вы бы захотели использовать блок finally, чтобы не допустить утечки обработчика событий.) Если бы мы не отказались от подписки, то BandwidthUI
они просуществовали бы как минимум столько же, сколько и служба передачи.
Лично я редко сталкиваюсь с этим - обычно, если я подписываюсь на событие, цель этого события живет, по крайней мере, столько же, сколько издатель - например, форма будет длиться столько же, сколько и кнопка, которая на ней. Об этой потенциальной проблеме стоит знать, но я думаю, что некоторые люди беспокоятся об этом, когда в этом нет необходимости, потому что они не знают, в какую сторону идут ссылки.
РЕДАКТИРОВАТЬ: это ответ на комментарий Джонатана Дикинсона. Во-первых, посмотрите документацию для Delegate.Equals (object), которая явно дает поведение равенства.
Во-вторых, вот короткая, но полная программа, показывающая, как работает отмена подписки:
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();
}
}
Полученные результаты:
Raising Foo
Subscriber.FooHandler()
Raising Foo
No handlers
(Проверено на Mono и .NET 3.5SP1.)
Дальнейшее редактирование:
Это должно доказать, что издателя событий можно собрать, пока есть ссылки на подписчика.
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);
}
}
Результаты (в .NET 3.5SP1; здесь Mono ведет себя немного странно. Мы рассмотрим это как-нибудь):
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