就你而言,一切都很好。这是一个物体出版保持的事件targets事件处理程序的实时状态。所以如果我有:
publisher.SomeEvent += target.DoSomething;
then publisher
有参考target
但反之则不然。
在您的情况下,发布者将有资格进行垃圾收集(假设没有其他对它的引用),因此它具有对事件处理程序目标的引用这一事实是无关紧要的。
棘手的情况是当发布者寿命很长但订阅者不想加入时that如果您需要取消订阅处理程序。例如,假设您有一些数据传输服务,可以让您订阅有关带宽更改的异步通知,并且传输服务对象是长期存在的。如果我们这样做:
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
至少会和接送服务一样长。
就我个人而言,我很少遇到这种情况 - 通常,如果我订阅一个事件,该事件的目标至少与发布者一样长 - 例如,表单将与其上的按钮一样长。了解这个潜在问题是值得的,但我认为有些人在不必要的时候会担心它,因为他们不知道引用的方向。
EDIT:这是对乔纳森·狄金森评论的回应。首先,查看文档委托.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();
}
}
Results:
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