В твоем случае все нормально. Это объект, который публикует события, которые поддерживают цели обработчиков событий. Так что если у меня есть:
publisher.SomeEvent += target.DoSomething;
затем 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 (объект) , которые явно дают поведение равенства.
Во-вторых, вот короткая, но полная программа, показывающая работоспособность отписки:
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