在你的情况下,一切都很好。它是发布事件的对象,它使事件处理程序的目标保持活动状态。所以如果我有:
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 将至少与传输服务一样长。
就我个人而言,我很少遇到这种情况——通常,如果我订阅一个事件,该事件的目标至少与发布者一样长——例如,一个表单的持续时间与它上面的按钮一样长。这个潜在问题值得了解,但我认为有些人在不需要时会担心它,因为他们不知道参考文献的走向。
编辑:这是为了回答 Jonathan Dickinson 的评论。首先,查看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