就您而言,一切都很好。它是 发布 事件的 对象 ,使事件处理程序的 目标 保持活动状态。所以,如果我有:
publisher.SomeEvent += target.DoSomething;
然后
publisher引用
target但并非相反。
在您的情况下,发布者将有资格进行垃圾回收(假定没有其他引用),因此它与事件处理程序目标的引用无关。
棘手的情况是发布者的寿命很长,但订阅者却不想这样做-在 这种
情况下,您需要取消订阅处理程序。例如,假设您有一些数据传输服务,该服务使您可以订阅有关带宽更改的异步通知,并且该传输服务对象是长期存在的。如果我们这样做:
BandwidthUI ui = new BandwidthUI();transferService.BandwidthChanged += ui.HandleBandwidthChange;// Suppose this blocks until the transfer is completetransferService.Transfer(source, destination);// We now have to unsusbcribe from the eventtransferService.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 FooSubscriber.FooHandler()Raising FooNo 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~PublisherFoo==null ? FalseEnd of Main method. Subscriber is about to become eligible for collection~Subscriber



