У вашому випадку все добре. Це об'єкт, який публікує події, який зберігає цілі обробників подій. Тож якщо я маю:
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;
(Ви насправді хочете використати остаточний блок, щоб переконатися, що ви не просочилися обробником події.) Якщо ми не скасували підписку, то програма 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; Моно, як видається, поводиться тут трохи дивно. Це буде розглянуто деякий час):
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