C# 匿名事件处理程序和处理程序

C# 匿名事件处理程序和处理程序,c#,disposing,C#,Disposing,我有一个关于匿名事件处理程序的简短问题: 这是我的代码: public void AddTestControl(Control ctrl) { ctrl.Disposed += (o, e) => { RemoveTestControl(ctrl); }; ctrl.SomeEvent += _Control_SomeEvent; } public void RemoveTestControl(Control ctrl) { ctrl.SomeEvent -= _

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

这是我的代码

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

public void RemoveTestControl(Control ctrl)
{
    ctrl.SomeEvent -= _Control_SomeEvent;
}
这段代码是否正常,还是应该重写代码以删除已处理的事件处理程序? 大概是这样的:

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,发布者(即
控件
对象)很可能是订阅者的子对象(可能是
页面
表单
),这将是第一个带着所有相关对象离开范围的对象。因此,无需删除事件处理程序

取消订阅事件总是让我感觉更干净,即使在我知道订阅者总是比发布者(引发事件的对象)寿命长的情况下:事件将不再引发,发布者不再可访问且可以收集

然后再说一遍,有多少人因为取消订阅WinForms应用程序中的每个事件处理程序而遇到麻烦?对象引用从发布服务器指向订阅服务器,而不是从订阅服务器指向订阅服务器,因此可以在订阅服务器运行时收集发布服务器。它并不存在与相反情况相同的危险,即一个长期存在的发布者(例如静态事件)可能会在可能收集到潜在的大型订阅者之后很长时间内保持其生存状态


如果您想要/需要取消订阅,那么取消订阅同一委托的要求会让匿名事件处理程序有点痛苦。解决这个问题的方法很简单:订阅时返回一个
IDisposable
,在释放时取消订阅,而不必记住您订阅的代理。将您所有的订阅内容放入一个
复合可编程文件中
只需一个
Dispose
电话即可取消订阅所有内容。

两个codez都可以,但出于个人喜好,我喜欢第二个。它读起来比第一个更清楚

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

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

简言之: *最好将dispose事件单独留在ChildControl上,并在ChildControl.dispose中执行所有清理
*没有必要取消订阅活动。事件调度器将检查订阅服务器是否处于活动状态

但我的问题更多的是关于这个事件。我需要自己分离还是第一个代码示例就可以了?恐怕没有人承诺事件处理程序的执行顺序。@juFo假设你问这个问题的原因是担心内存泄漏,那么不,你不需要自己分离事件处理程序。由于
控件
对象与包含事件处理程序的
页面
表单
具有相同的生存期,因此它们将一起超出范围。不过,您需要在回答中切换单词“publisher”和“subscriber”。发布者持有对订阅者的引用(在支持事件的委托的调用列表中),因此长期存在的发布者不必要地保持订阅者的活动状态。@Anton你完全正确。谢谢你指出这一点。