匿名事件处理程序和处理

关于匿名事件处理程序,我有一个相当简短的问题:

这是我的代码

public void AddTestControl(Control ctrl) { ctrl.Disposed += (o, e) => { RemoveTestControl(ctrl); }; ctrl.SomeEvent += _Control_SomeEvent; } public void RemoveTestControl(Control ctrl) { ctrl.SomeEvent -= _Control_SomeEvent; } 

此代码是否正常,或者是否应重写代码以删除Disposed Event Handler? 像这样的东西:

 public void AddTestControl(Control ctrl) { ctrl.Disposed += _Control_Disposed; ctrl.SomeEvent += _Control_SomeEvent; } public void RemoveTestControl(Control ctrl) { ctrl.Disposed -= _Control_Disposed; ctrl.SomeEvent -= _Control_SomeEvent; } 

通常,您需要从对象中删除事件处理程序以使其符合垃圾回收的唯一情况是发布者对象(定义事件的对象)比订阅者对象(包含事件的对象)更长处理程序)。 在这种情况下,当用户超出范围时,GC将无法释放用户,因为它仍然被发布者引用。

在这种情况下,假设这是WebForms或WinForms,发布者(即Control对象)很可能是订阅者的子对象 (可能是PageForm ),这将是第一个超出范围的人所有与之相关的对象。 因此, 不需要删除事件处理程序

即使在我知道订阅者总是比发布者 (提升事件的对象)更长的情况下,取消订阅事件对我来说总是感觉更干净:事件永远不会再次提出,发布者不再可以访问并且可以被收集。

那么 ,有多少人在例如WinForms应用程序中取消订阅每个事件处理程序的麻烦? 对象引用从发布者到订阅者,而不是相反,因此可以在订阅者生存时收集发布者。 它不会产生与相反情况相同的危险,在这种情况下,长期存在的发布者(例如静态事件)可能浪费地使潜在的大订户在收集后很长时间内保持活跃状态​​。

如果您想要/需要取消订阅,那么取消订阅同一个委托的要求会使匿名事件处理程序变得有点痛苦。 Reactive Extensions以一种巧妙的方式解决了这个问题:订阅时不是必须记住您订阅的委托,而是返回IDisposable ,该IDisposable在处理时取消订阅。 将所有订阅放入CompositeDisposable ,只需一次Dispose调用即可取消订阅所有内容。

两个codez都很好,但我喜欢第二个作为个人喜好的问题。 它比第一个更清晰。

在第一个代码的顶部,有一个匿名的lambda委托,它获得了对ctrl的当前引用。 该代码可能会出现意外行为,具体取决于具体情况和编译优化设置:是否内联调用。

更不用说代码存在架构问题:你有ControlOwner和一堆子控件。 我认为你在运行时向ControlOwner添加子控件,然后通过将ControlOwner订阅到childControl事件来尝试对其行为做出反应。 这对于像_ButtonClicked等事件来说很好。但对Dispose来说并不好。 让子控件处理它自己处理,OwnerControl不需要知道它。 更不用说它可能一次不存在ChildControl [n] .Dispose被调用。

简而言之:*最好将dispose事件单独留在ChildControl上,并在ChildControl.Dispose *中进行所有清理,不必取消订阅事件。 事件调度员将检查订户是否还活着。