Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/331.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为动态创建的项目释放事件处理程序_C#_Winforms_Memory Leaks_Toolstripmenu - Fatal编程技术网

C# 为动态创建的项目释放事件处理程序

C# 为动态创建的项目释放事件处理程序,c#,winforms,memory-leaks,toolstripmenu,C#,Winforms,Memory Leaks,Toolstripmenu,我正在用XML文件中的条目填充ToolStripMenu。添加到菜单中的每个ToolStripItem都会自动获得分配给它的十几个事件处理程序。用户可以选择更改XML并刷新菜单,使用以下方法清除所有内容: menuStrip1.Items.Clear(); 这将从XML重新填充并刷新表单。所有这些动态项的事件处理程序似乎从未从内存中删除。大多数项目并不直接位于menuStrip1上,而是位于动态创建的子菜单中,这些子菜单位于menuStrip1上 尽管如此,我仍然可以坐在那里刷新表单,而内存只

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

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

尽管如此,我仍然可以坐在那里刷新表单,而内存只是失控膨胀,每次刷新大约
100个dynamic ToolStripItems,就会增加
5-10 MB
它可以很快达到数百MB,这会使应用程序崩溃

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


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

如果您同时处理/删除事件处理程序及其订阅的对象,您应该可以。否则,是的,你将不得不取消每个处理程序


或者看看另一种订阅方式——不是编译器生成的事件。查看示例)

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

已更新

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

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
    }
}

我经常使用的一个技巧是创建一个
private\u cleanUp=New List()
class级别的变量,并使用它来清理事件处理程序

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

this.NameTextbox.Click += My_Handler;
_cleanUp.Add(() => this.NameTextbox.Click -= My_Handler);
然后在清理时,对于您的代码,我可以这样做:

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

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

您可能应该对其中的每一个调用
Dispose
,以释放它们的资源。如果您同时处理/删除事件处理程序及其订阅的对象,您应该可以。否则,是的,您可能必须取消订阅每个处理程序(或者查看订阅的不同方式——而不是编译器生成的事件),您可能希望查看“查看”对话框,并且您中的一位应该做出回答,以便我可以标记为已回答。长话短说,C#似乎不像我想象的那样勤于记忆。感谢您的教训。感谢您的建议,但这只会直接触发menuStrip1上的菜单,实际上是菜单和子菜单中的项目具有所有事件处理程序。现在我发现的唯一一件事是递归地遍历菜单层次结构,并分别处理每个项目,这是一件痛苦的事情,因为每个菜单都有几种不同类型的ToolStripItems,这是一件令人头痛的事情。