如何修复由ContextMenuStrip引用的对象引起的内存泄漏
本文关键字:内存 泄漏 对象 ContextMenuStrip 引用 何修复 | 更新日期: 2023-09-27 18:12:06
我使用dotMemory来定位内存泄漏。我想要消失的对象是由一个事件处理程序通过ToolStripMenuItem和ContextMenuStrip引用。对象包含以下属性:
public override ContextMenuStrip PopupMenu
{
get
{
ContextMenuStrip myPopup = new ContextMenuStrip();
myPopup.Items.Add(ItemDelete);
return myPopup;
}
}
public ToolStripMenuItem ItemDelete
{
get
{
ToolStripMenuItem itemDelete = new ToolStripMenuItem("Delete " + name);
itemDelete.Enabled = Deletable;
itemDelete.Image = Properties.Resources.del;
itemDelete.Click += ItemDelete_Click;
return itemDelete;
}
}
我已经简化了代码,弹出式菜单有大约十几个菜单项,在我使用弹出式菜单删除对象后,它们似乎都抓住了这个对象。我已经尝试重写对象的基delete方法来删除处理程序,但这不起作用。
public override void delete()
{
if (PopupMenu != null)
{
ItemDelete.Click -= ItemDelete_Click;
}
base.delete();
}
如果没有一个好的,最小的, 完整的代码示例,就不可能确切地知道问题是什么,更不用说最佳解决方案了。said…
基于你到目前为止发布的少量代码,看来你滥用了c#中的属性特性,并且这样做已经使代码变得足够模糊,以至于你无法识别错误。特别是,你的ItemDelete
属性每次调用时都会创建一个新对象,这是实现这种getter的可怕方式。为了使代码正常工作,您必须小心地只调用一次属性getter,并在其他地方手动缓存结果。
在属性getter中创建新对象本身并不坏,但只有当该对象将被属性getter本身缓存并在后续调用中重用时才应该这样做,或者该对象在语义上是一个简单的值(最好是实际值类型,但简单的不可变引用类型也可以)。新创建的对象在功能上与之前创建的对象相同(即它们可以互换使用而不影响代码的正确性)。
鉴于上述情况,可能您会发现以下替代方案是解决问题的有效方法:
private Lazy<ToolStripMenuItem> _itemDelete =
new Lazy<ToolStripMenuItem>(() => _CreateItemDelete());
private ToolStripMenuItem _CreateItemDelete()
{
ToolStripMenuItem itemDelete = new ToolStripMenuItem("Delete " + name);
itemDelete.Enabled = Deletable;
itemDelete.Image = Properties.Resources.del;
itemDelete.Click += ItemDelete_Click;
return itemDelete;
}
public ToolStripMenuItem ItemDelete
{
get
{
return _itemDelete.Value;
}
}
这将延迟ToolStripMenuItem
对象的创建,直到第一次调用属性getter,但将在后续调用getter时返回第一次调用时创建的对象。
通过这种方式,您将确保稍后执行语句ItemDelete.Click -= ItemDelete_Click;
时,您实际上是从原始对象中删除事件处理程序,而不是从当时创建的某个新对象中删除事件处理程序。