C# 是否需要删除析构函数中的事件处理程序?

C# 是否需要删除析构函数中的事件处理程序?,c#,wpf,events,event-handling,destructor,C#,Wpf,Events,Event Handling,Destructor,我使用一些UserControls,它们在运行时在我的应用程序中被创建和销毁(通过创建和关闭包含这些控件的子窗口)。 它是一个WPF用户控件,继承自System.Windows.Controls.UserControl。没有我可以覆盖的Dispose()方法。 PPMM是一个与我的应用程序具有相同生命周期的Singleton。 现在,在我的(WPF)UserControl的构造函数中,我添加了一个事件处理程序: public MyControl() { InitializeCompone

我使用一些
UserControls
,它们在运行时在我的应用程序中被创建和销毁(通过创建和关闭包含这些控件的子窗口)。
它是一个WPF用户控件,继承自
System.Windows.Controls.UserControl
。没有我可以覆盖的
Dispose()
方法。
PPMM
是一个与我的应用程序具有相同生命周期的
Singleton

现在,在我的(WPF)
UserControl
的构造函数中,我添加了一个事件处理程序:

public MyControl()
{
    InitializeComponent();

    // hook up to an event
    PPMM.FactorChanged += new ppmmEventHandler(PPMM_FactorChanged);
}
我习惯于在析构函数中删除此类事件处理程序:

~MyControl()
{
    // hook off of the event
    PPMM.FactorChanged -= new ppmmEventHandler(PPMM_FactorChanged);
}
今天,我偶然发现了这一点,并想知道:

1)这是必要的吗?还是由总承包商负责?

2)这是否有效?或者我必须存储新创建的
ppmmEventHandler


我期待着你的回答。

如果代码到达析构函数,那就不重要了

这是因为它只有在不再收听任何事件时才会被销毁。

如果它仍然在监听事件,它就不会被销毁。

因为
PPMM
是一个长寿命的对象(singleton),那么这个代码就没有什么意义了

这里的问题是,只要该事件处理程序引用该对象,它就没有资格进行垃圾收集,至少只要拥有该事件的另一个对象处于活动状态

因此,将任何内容放入析构函数都是毫无意义的,因为:

  • 事件处理程序已被删除,因此该对象有资格进行垃圾收集
  • 事件处理程序没有被删除,拥有的对象没有资格进行垃圾收集,因此终结器将永远不会被调用
  • 这两个对象都有资格进行垃圾收集,在这种情况下,您不应该访问终结器中的另一个对象,因为您不知道它的内部状态
  • 简而言之,不要这样做

    现在,当您实现
    IDisposable
    时,可以对向
    Dispose
    方法添加这样的代码提出不同的观点。在这种情况下,它完全有意义,因为它的用户代码在预定义的受控点调用
    Dispose

    但是,只有当对象符合垃圾收集条件并且具有终结器时,才会调用终结器(析构函数),在这种情况下没有意义

    至于问题nbr。2,我把它理解为“我可以取消订阅这样的活动吗”,那么是的,你可以。只有在围绕匿名方法或lambda表达式构造委托时,才需要保留用于订阅的委托。当您围绕现有方法构建它时,它将起作用


    编辑:WPF。对,没看到那个标签。对不起,我其余的回答对WPF没有多大意义,因为我不是WPF大师,我真的不能说。然而,有一种方法可以解决这个问题。这是完全合法的,所以如果你能改进的话,就把另一个答案的内容挖出来。因此,如果有人知道如何正确地使用WPF用户控件来实现这一点,您可以自由地阅读我的答案的整个第一部分,并添加WPF的相关部分

    编辑:让我也来回答这里评论中的问题

    由于所讨论的类是一个用户控件,因此它的生存期将绑定到一个窗体。当窗体关闭时,它将处理它所拥有的所有子控件,换句话说,这里已经存在一个dispose方法

    如果用户控件管理自己的事件,则处理此事件的正确方法是在Dispose方法中取消钩住事件处理程序

    (其余部分已移除)

    首先,我要说的是,不要使用析构函数,而是Dispose()来清除资源

    其次,在我看来,如果此代码位于一个经常创建且生命周期很短的对象中,那么最好自己删除事件处理程序,因为这是一个到holder对象的链接,这将阻止GC收集它

    问候。

    2)这确实有效


    1) 我有一个案例(使用应用内消息服务),事件处理程序指向未发布的全局对象,因此GC无法收集该对象。我认为这通常是一种罕见的情况——如果你认为这种情况发生在你身上,那么使用像红门蚂蚁这样的分析器,你可以轻松地进行内存分析。

    是不是
    PPMM
    MyControl
    实例寿命更长的外部工具

    如果是这样,除非
    PPMM_FactorChanged
    是一个静态方法,否则
    ppmmEventHandler
    将保留对
    MyControl
    实例live的引用-这意味着该实例将永远无法进行垃圾收集,并且终结器也永远不会启动


    您不需要为删除代码保留
    ppmmEventHandler

    GC将处理该问题。虽然事件持有强引用,但它仅在父对象本身上持有强引用。最后,只有MyControl将通过事件处理程序保留引用,因此GC将收集它


    另一方面,使用终结器,它不是描述器。是因为这种坏习惯。如果你想注销一个事件,你应该考虑<代码> IDISPOSTABLE 。

    事件处理程序是棘手的,并且可以很容易地隐藏资源泄漏。正如蒂格兰所说。使用IDisposeable并忘记析构函数。我建议测量一下你是否做对了。只要在task manager中查看应用程序的内存消耗情况,就可以判断是否存在泄漏,如果您通过加载并关闭数千个窗口对其进行一点压力测试

    WPF不支持
    IDisposable
    well。如果您正在实现一个需要清理的WPF控件,那么应该考虑挂接到and