【问题标题】:When and where to call the RemoveHandler in VB.NET?在 VB.NET 中何时何地调用 RemoveHandler?
【发布时间】:2010-04-27 12:45:53
【问题描述】:

我正在开发 .NET 1.1 中的 VB.NET Windows 窗体项目。我有这种架构,非常简单。

Public MustInherit Class BaseTestLogic

  Private _TimerPoll As Timer

  Public Sub New(ByVal sym As SymbolFileMng, ByVal cfg As LampTestConfig, ByVal daas As DaasManager, ByVal mcf As Elux.Wg.Lpd.MCFs.VMCF)

    AddHandler _TimerPoll.Tick, AddressOf TimerPoll_Tick

  End Sub

End Class

Public Class SpecificTestLogic
  Inherits BaseTestLogic      

End Class

根据我正在做的测试类型,我创建了一个从 BaseTestLogic 派生的特定测试的实例。但我发现,在创建数百个对象后,我可能会遇到 StackOverflow 异常。

我检查了我的代码,发现我忘记删除处理程序到 Timer Tick。问题是,何时何地移除 hadler 是正确的?

是否需要在基类中实现IDisposable接口,在Dispose中实现RemoveHandler?

【问题讨论】:

    标签: .net vb.net event-handling dispose


    【解决方案1】:

    您可以在调用 Dispose 时删除处理程序,但纯粹主义者会说“您不应该滥用 IDisposable 来处理非托管资源之外的其他目的”。

    另一种选择是在 Finalize 方法中删除处理程序。

    如果在您的设计中有意义的话,您也可以在几个不同的地方移除处理程序。删除已删除的处理程序不会导致任何问题 - 除非该事件是自定义事件并且其 AddHandler/RemoveHandler 实现与非自定义事件的行为不匹配(只需使用 [Delegate].CombineDelegate/[Delegate] 。消除)。只是不要告诉你纯粹的朋友。他们不会遵守的。

    【讨论】:

    • 这很奇怪,但在我的情况下,终结器从未被调用,可能是什么原因?
    • 原因很可能是因为某些可达对象仍然持有对 BaseTestLogic 对象或其成员之一的引用(包括事件处理程序)。一个对象只有在没有(强)引用(并且垃圾收集器决定它需要删除该对象)后才被最终确定。
    • 最后我在 Dispose 中实现了 Disposable 模式和 RemoveHandler to Tick 事件。现在问题似乎解决了。我不是一个纯粹主义者,幸运的是,我也不是和我一起工作的同事:D
    • MA Hainn 的意思是,但不是直接说,如果没有调用终结器,您可能已经有资源泄漏(本质上是内存泄漏的托管版本)。追踪这些可能很棘手,使用分析器可以提供很大帮助,但在您了解导致对象寿命超过其应有时间的原因之前,它们可能看起来像幽灵般的问题。
    • 在接收事件的对象的 Finalize 方法中取消订阅事件是没有用的。只要事件发布者(除非它取消订阅),事件订阅者将始终保持活动状态。如果对象已最终确定,那将是因为事件发布者本身超出了范围,届时取消订阅将变得毫无意义。此外,任何说不应在 Dispose 中取消订阅事件的“纯粹主义者”都是错误的。 事件是非托管资源。托管资源是可以通过 finalize 处理的资源。由于事件不能,因此它们是不受管理的。
    【解决方案2】:

    如果您在构造函数中添加它们,在 Dispose 中删除它们感觉不错,但这当然取决于您的设计。

    这是一个问题,其中包含有关何时需要担心删除它们的信息
    AddHandler/RemoveHandler Not Disposing Correctly

    【讨论】:

    • 我已经阅读了链接的问题,但并没有太大帮助。我知道我应该删除处理程序,但不确定在哪里。 Dispose 可能是一个想法,我想确定它是最佳实践。
    • 在我看来,将它们添加到 Dispose 的一个很好的理由是,您班级的用户可以使用 Using。
    【解决方案3】:

    我的第一个想法是,您的实际问题与添加和删除事件处理程序几乎没有关系。 StackOverflowException 意味着您有一系列函数正在创建无限循环的递归调用。您发布的代码没有显示任何可能发生的情况,但异常的堆栈跟踪应该将您指向有问题的代码。

    根据您关于创建派生类型测试的评论,我想知道您是否可以在基类中发布更多来自构造函数的代码。

    【讨论】:

    • @Gideon Engelberth: StackOverflowException 意味着堆栈溢出。这通常是因为无限循环/递归,但这仅仅是因为无限循环/递归会迅速溢出堆栈。请注意:它们不是一回事,无限循环/递归不是 StackOverflow 异常的含义。
    • @M.A. Hanin:也许我应该说“通常意味着”,但是每当我看到堆栈溢出异常时,我都会寻找递归,因为它是迄今为止此类异常的最常见原因。
    • 我认为@GideonEngelberth 是正确的,因为TimerPoll_Tick 处理程序中必须存在某种形式的递归,因为Timer 包装器的方法调用泛滥,以便引用更大的生命周期对象,或静态引用,最有可能托管TimerPoll_Tick 事件处理程序。
    【解决方案4】:

    这是一个老问题,但我是通过网络搜索找到的,所以它仍然是一个关于计时器的有用问题。没有对您的潜在问题给出适当的答案。已为您的问题提供了答案,但问题是错误的。您的问题应该是:如何禁用计时器以使事件不再触发?

    Dim WithEvents timer1 as new Timer()

    (然后将 timer1_Elapsed 添加到您的代码中)

    这解决了担心 IDisposable 或 Finalize 的问题,因为 Timer 事件处理程序是为您管理的。当您不再需要计时器时,设置 Enabled = False 或调用 Stop() 方法以使其停止计时。

    【讨论】:

    • 重要的是要注意,如果计时器没有被禁用,它持有的任何引用都将永远保持活动状态,无论它是否具有任何明显有用的效果。如果不存在对由事件互连的任何对象的外部引用,则禁用计时器并放弃它将清除事件,但最好将timer1 设置为Nothing(这将断开事件处理程序)而不是希望它们断开连接.
    【解决方案5】:

    Timer 类设计者实现IDisposable 的决定必须被视为一种声明,即实现处理某种形式的对非.NET API的引用> / raw 操作系统资源分配(对不起,非规范面额)。

    正如@supercat 提到的,只要事件发布者存在,事件订阅者将始终保持活动状态,这导致引用更大生命周期对象的问题,如In vb.net, if I use AddHandler, Do I have to use RemoveHandler? 中所述,不是 因为事件处理程序是非托管资源

    事件处理程序不是在.NET 术语中使用的意义上的“非托管资源”,而不是“托管”:不需要通过任一 @ 显式分配管理987654326@或Dispose()资源释放和清理。

    来自 Troelsen 2010 Pro C#:

    终结器可用于释放 垃圾收集器启动时的非托管资源。但是, 鉴于许多非托管对象是“珍贵物品”(例如原始 数据库或文件句柄),尽快释放它们可能很有价值 尽可能而不是依赖垃圾收集来发生。作为一个 替代覆盖Finalize(),你的类可以实现 IDisposable 接口,它定义了一个名为 Dispose().

    勘误表:见Is unwired event a memory leak? 所以,回到问题,如果TimerPoll_Tick 事件处理程序属于一个更大的生命周期对象,BaseTestLogic 实例永远不会被释放。我不确定,但即使TimerPoll_TickBaseTestLogic 静态方法,也可能会发生这种情况。

    回到这个问题,stackoverflow 异常似乎很奇怪,并且可能与TimerPoll_Tick 事件处理中的某种形式的设计失败更相关,而不是像@GideonEngelberth 建议的对象生命周期问题。

    【讨论】:

      【解决方案6】:

      这很奇怪,但在我的情况下,终结器从未被调用,可能是什么原因?

      或者 GC.SupressFinalize() 在你的代码中被调用了?

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-11-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-04-02
        • 2021-12-27
        相关资源
        最近更新 更多