【问题标题】:Releasing event handlers for dynamically created items为动态创建的项目释放事件处理程序
【发布时间】:2014-02-20 12:51:44
【问题描述】:

我正在使用来自 XML 文件的条目填充 ToolStripMenu。添加到菜单中的每个ToolStripItem 都会自动获得大约十几个分配给它的事件处理程序。用户可以选择更改XML 并刷新菜单,这会清除所有内容:

menuStrip1.Items.Clear();

这将从XML 重新填充并刷新表单。似乎所有这些动态项的事件处理程序从未从内存中删除。大多数项目并不直接位于 menuStrip1 上,而是位于位于menuStrip1 上的动态创建的子菜单中。

尽管如此,我可以坐在那里刷新表单并且内存只是膨胀失控,每次刷新大约 100 dynamic ToolStripItems. 添加5-10 MB 它可以迅速达到数百 MB 导致应用程序崩溃。

ToolStripItems 还加载其他内容,例如属性和图标,但似乎是事件处理程序使用了大部分添加的内存。我注释掉了所有的处理程序,每次刷新时内存变得更加(但不是完全)可持续。

是否有必要枚举所有菜单和子菜单并单独释放每个事件处理程序以确保它不会留下来?

【问题讨论】:

  • 您可能应该对每个人调用Dispose 以释放他们的资源。
  • 如果你同时处理/删除事件处理程序它订阅的对象,你应该没问题。否则,是的,您可能必须取消订阅每个处理程序(或查看不同的订阅方式——不是编译器生成的事件)
  • 您可能想查看Weak Event Pattern
  • 你们中的一个人应该回答,以便我可以标记为已回答。长话短说似乎 C# 并不像我想象的那样勤奋记忆。谢谢你的课。

标签: c# winforms memory-leaks toolstripmenu


【解决方案1】:

如果您同时处置/删除事件处理程序和它订阅的对象,您应该没问题。否则,是的,您将不得不取消订阅每个处理程序。

或者看一下不同的订阅方式——不是编译器生成的事件。示例见http://paulstovell.com/blog/weakevents

【讨论】:

    【解决方案2】:

    检查菜单条的ItemRemoved 事件。它将返回一个从您的菜单条中删除的项目。您可以在此处处理它,也可以从此处删除每个处理程序。

    更新

    获得父菜单后,您可以使用递归函数调用删除所有子菜单的事件处理程序。

    private void menuStrip1_ItemRemoved(object sender, ToolStripItemEventArgs e)
    {
        foreach (object child in ((ToolStripMenuItem)e.Item).DropDownItems)
        {
            if (child.GetType().Name == "ToolStripMenuItem")
                RemoveHandler((ToolStripMenuItem)child);                
        }
    }
    private void RemoveHandler(ToolStripMenuItem item)
    {
        if (item.HasDropDownItems)
        {
            foreach (Object dropdown in item.DropDownItems)
            {
                if (dropdown.GetType().Name == "ToolStripMenuItem")
                    RemoveHandler((ToolStripMenuItem)dropdown);
            }
        }
        else
        {
            //item.Click -= new EventHandler(MenuItem_Clicked);
            //Remove event handlers
        }
    }
    

    【讨论】:

    • 感谢您的建议,但这只会触发直接在 menuStrip1 上的菜单,实际上是菜单和子菜单中的项目具有所有事件处理程序。现在我发现的唯一一件事就是递归地遍历菜单层次结构并单独处理每个项目,这很痛苦,因为每个菜单都有几种不同类型的 ToolStripItems,这是一个令人头疼的问题。
    【解决方案3】:

    我经常使用的一个技巧是创建一个private _cleanUp = New List<Action>() 类级变量并将其用于清理事件处理程序。

    因此,当我附加事件处理程序时,我还将分离代码添加到列表中,如下所示:

    this.NameTextbox.Click += My_Handler;
    _cleanUp.Add(() => this.NameTextbox.Click -= My_Handler);
    

    然后在清理时,对于您的代码,我可以这样做:

    _cleanUp.ForEach(a => a());
    _cleanUp.Clear();
    menuStrip1.Items.Clear();
    

    然后我就不必确切地记住添加了什么。我可以将任何我喜欢的清理代码添加到列表中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-04-13
      • 1970-01-01
      • 2013-09-23
      • 1970-01-01
      • 2013-12-24
      • 1970-01-01
      • 2017-07-29
      • 2019-01-29
      相关资源
      最近更新 更多