【问题标题】:Do I have to unsubscribe from button events after using it in c#?在 c# 中使用按钮事件后是否必须取消订阅?
【发布时间】:2018-03-22 21:04:27
【问题描述】:

我正在使用 Windows 窗体。

我的 C# 应用程序包含 100 个user controls。我会在需要时显示/隐藏这 100 个 user controls 中的一个并隐藏其余的。 每个user controls 都有30 个buttons,我在构造函数中订阅button 事件,如下所示:

public UserControl1()
{
    InitializeComponent();

    button1.Click += new EventHandler(MyButtonClick);
    button2.Click += new EventHandler(MyButtonClick);
        .

        .
    button30.Click += new EventHandler(MyButtonClick);
}

void MyButtonClick(object sender, EventArgs e)
{
    // do something
}

因此,当我运行应用程序时,所有 100 个User controls 都订阅了 30 个buttons 事件,一些user controls 订阅了该事件,但它们在应用程序的使用过程中从未使用过。

我读到了一些关于取消订阅事件hereHere 的内容,但有些答案说你应该取消订阅,因为它会导致内存泄漏,有些人说你不必这样做,因此答案仍然不清楚。

我的问题是我是否必须在使用 button 事件后取消订阅它,例如:当我显示/隐藏 user control 时。如果是,我如何在显示user control 时订阅button 事件并在未显示时取消订阅。

【问题讨论】:

  • 难道没有可以用来分别附加/分离事件处理程序的显示/隐藏事件吗?
  • @PeteGO 我现在看了看,我什至没有发现
  • 您可以使用 IsVisibleChanged 事件。

标签: c#


【解决方案1】:

为什么要退订?

与订阅 (Button) 相比,它归结为订阅者 (UserControl) 的生命周期。 subscribe 持有对订阅者的引用。因此,如果订阅者的生命周期更长,那么订阅者将内存泄漏订阅者。

因此,在您的情况下,您应该询问按钮是否会比 UserControl 持续更长时间。如果按钮的使用寿命较短,则无需取消订阅。否则会导致 UserControl 内存泄漏。

在你的情况下,我猜你不需要取消订阅。

【讨论】:

  • 很好的解释,但我仍然不明白的是:我怎么知道订阅(按钮)是否比订阅者(userControl)有更长的寿命? .我不处理用户控件,所有按钮仍然存在,所以谁的寿命比谁长?
  • 好吧,如果控件包含按钮,我怀疑就是这种情况,那么它们的使用寿命相同,您无需取消订阅。
【解决方案2】:

如果我的问题没有错 - 每个控件都只订阅它自己的孩子(30 个按钮)。忘记取消订阅的情况是个坏主意,即发布者(按钮)的寿命将长于订阅者(用户控制)。为什么?因为发布者将存储指向订阅者的链接,并将阻止该订阅者被垃圾收集器处理。 在您的情况下,按钮的寿命永远不会超过它的父用户控件,因此您无需取消订阅。

【讨论】:

    【解决方案3】:

    在你的情况下,没有理由担心。您链接到的两个示例具有不同的“类型”事件,例如:如果对象 A 发生更改,则应调用方法 X,以便 X 可以对对象 A 中的更改进行操作或处理。 例如 A = 产品库存;如果 A 的计数变为零,则它不能再出售,或者必须订购新的物品,等等。

    当响应更改的数据时,调用 X 的需要可能会在某个时间点停止存在,然后注销事件可能是有益的,尤其是在没有其他方法可以中断或禁用链接的情况下。

    但是,在像您这样的用户界面场景中,调用 X 的需求通常仍然存在。如果需要,它可以通过其他方式进行控制,例如通过禁用按钮或隐藏它。在按钮和方法之间建立链接真的没有什么昂贵的。断开链接的唯一原因是“如果单击按钮,我不再希望永远调用 X”。

    【讨论】:

      【解决方案4】:

      通常不必取消订阅事件。也就是说,有特殊情况需要这样做。

      这通常看起来像这样:您有一个长寿命对象,它创建短寿命对象并附加它们的方法来处理其(或其他一些长寿命对象的)事件。或者,您创建一个对象并将其方法附加到具有更长生命周期的不同对象的事件。

      现在,当这些短期对象超出范围时,它们仍将附加到事件,因此只要长期对象(它们订阅的事件)还在,Garbage Collector 就不会回收它们在适用范围。这是因为它保留了对应该处理其事件的对象的引用。

      在您的情况下(以及大多数其他典型情况),处理程序是拥有子控件的类的成员,因此不存在人为延长任何对象寿命的风险。您无需在此处明确取消订阅。

      【讨论】:

        【解决方案5】:

        您实际上并非必须取消订阅,但您应该这样做。取消订阅的方法是:

        //adding
        EventHandler myHandler = new EventHandler(MyButtonClick);
        button1.Click += myHandler;
        
        //removing
        button1.Click -= myHandler;
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-06-09
          • 1970-01-01
          • 2019-01-31
          • 2018-09-26
          • 2019-03-06
          • 1970-01-01
          相关资源
          最近更新 更多