【问题标题】:WinForms and event handlersWinForms 和事件处理程序
【发布时间】:2012-01-04 21:06:23
【问题描述】:

默认情况下,当您创建一个 Win-Form 应用程序时,这是由 Visual Studio 生成的用于处理 Form 的代码。

protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

这够了吗?或者我应该取消注册所有事件,以便控件准备好被垃圾收集器收集?

        if (disposing && (components != null))
        {
            myButton.OnClick-= MyFunction; //may be here!!
            // ... all events used
            components.Dispose();

        }

【问题讨论】:

    标签: c# winforms memory-management memory-leaks


    【解决方案1】:

    表单必须取消注册实体的所有事件,这些事件将超过表单。表单是否从与表单具有相同生命周期的实体中注销事件并不重要。无论出于何种原因,正常的教义似乎是“不要担心注销事件,除非它很重要”。我认为让所有订阅事件的对象在Dispose 上取消订阅它们会更干净,但不幸的是,vb.net 和 C# 都没有提供任何甚至远程干净的方法来实现这一点。使用实现IDisposable 的依赖项,可以将构造包装在一个例程中,该例程会将新创建的项目添加到稍后要清理的事物列表中。然后,只需对列表中的所有内容调用Dispose,即可执行所有必要的清理工作。不幸的是,从 .net 的角度来看,没有很好的方法来编写一个通用的例程,该例程将同时订阅一个事件并返回一个 Action<>IDisposable 或其他可用于取消订阅的对象。

    【讨论】:

      【解决方案2】:

      不,垃圾收集器会处理它。由于表单实例已被处置,而后者又处置了按钮,因此该事件再也无法引发。由于事件处理程序,表单和按钮之间存在循环引用,但垃圾收集器对它们没有任何问题。

      【讨论】:

      • 这是否意味着您不负责注销所有活动?
      • @Hans Passant:根据我的经验,如果表单中使用了有影响数量的事件,或者它使用得太频繁,而不是unsubscription,则导致到内存泄漏。
      • 是的,就是这个意思。仅当事件源的寿命超过事件使用者时才需要。表单及其子控件从来都不是问题,它们都会同时死亡。 SystemEvents 类的事件是必须手动取消订阅的事件示例。
      • @Tigran,“有影响力的数量”对我没有任何意义,抱歉。意大利面?
      • @HansPassant:是的,something 意大利语 :) 实际上,我的意思是,massive 数量的事件,只是用错了词。现在,没有办法纠正它。
      【解决方案3】:

      如果这是经常受到close and open 操作的表单,并且在其中使用大量事件,那么注销所有事件重要。因为事件肯定会消耗资源。

      如果这是一个会出现一段时间的表单,或者甚至永远不会出现,或者它是,比如说,你的应用程序的MainForm,那么取消订阅事件不重要

      place 在哪里可以做到这一点,我个人会在里面取消订阅,让我们说在Closing 覆盖和Dispose()

      【讨论】:

      • 为什么不在Dispose? .net 中有一个非常严格的约定,即当一个类实现 IDisposable 时,在实例上调用 Dispose 应该足以确保没有其他代表它的实体处于不良状态。对于事件发布者来说,存在已被放弃但永远不会取消订阅的订阅者是一种糟糕的状态——IDisposable.Dispose 应该阻止这种状态。
      • @supercat: 只是为了尽快摆脱它们,不要等待GC 调用,以防它不是由用户特别调用,或通过代码构造调用 (using陈述)。在Dispose() 中使用是一个有效的解决方案,顺便说一句,事实上我并没有像不可接受的那样拒绝它,只是就主题发表了我的看法。
      • 我并不是要暗示在调用Dispose 之前不应该断开事件,而是如果在调用Dispose 之前没有处理任何事件,@ 987654334@ 应该完成它们。请注意,Finalize() 通常对于清理事件毫无用处,因为它不会被调用,直到发布事件的对象被放弃渲染订阅没有实际意义,并且因为即使一个对象上的 Finalize() 揭示了其他一些事件应该被断开,事件取消订阅可惜不能保证线程安全。
      • @supercat:同意,Finalize()最糟糕的地方。事实上,我建议在Closing override 中编写该代码。
      猜你喜欢
      • 1970-01-01
      • 2022-01-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多