【发布时间】:2011-03-04 19:47:39
【问题描述】:
如果我创建一个 .NET 类,它使用这样的匿名函数订阅事件:
void MyMethod()
{
Application.Current.Deactivated += (s,e) => { ChangeAppearance(); };
}
此事件处理程序是否会成为防止我的班级被垃圾收集的根?
如果没有,哇!但如果是这样,你能告诉我删除语法吗?仅将 -= 与相同的代码一起使用似乎是错误的。
【问题讨论】:
如果我创建一个 .NET 类,它使用这样的匿名函数订阅事件:
void MyMethod()
{
Application.Current.Deactivated += (s,e) => { ChangeAppearance(); };
}
此事件处理程序是否会成为防止我的班级被垃圾收集的根?
如果没有,哇!但如果是这样,你能告诉我删除语法吗?仅将 -= 与相同的代码一起使用似乎是错误的。
【问题讨论】:
您可以使用 Simon D. 建议的“真实”方法,也可以这样做:
EventHandler handler = null;
handler = (s,e) => {
Application.Current.Deactivated -= handler;
ChangeAppearance();
};
Application.Current.Deactivated += handler;
由于这有点丑陋并且违背了使用 lambda 简洁的目的,我可能会将其重构为一个方法。但了解其他方法很有用。
警告:不言而喻,您必须非常小心,不要在订阅事件的时间点和订阅事件的时间点之间弄乱handler 的值实际调用,否则取消订阅部分将无法正常工作。
【讨论】:
我认为您确实需要取消订阅,因为事件提供者(应用程序)的生存时间(或至少可以生存)比您的消费者更长。因此,当应用程序仍然存在时,每个订阅实例都会死去,这会造成内存泄漏。
您正在为该事件订阅一个匿名代表。 这就是为什么你不能以同样的方式取消订阅它,因为你不能再解决它了。 实际上,您是在订阅的同时创建方法,并且您没有存储任何指向新创建方法的指针。
如果您稍微更改您的实现以使用“真实”方法,您可以像订阅它一样轻松地取消订阅该事件:
Application.Current.Deactivated += ChangeAppearance;
Application.Current.Deactivated -= ChangeAppearance;
private void ChangeAppearance(object sender, EventArgs eventArgs)
{
throw new NotImplementedException();
}
【讨论】:
你一定要清理参考。如果有任何疑问,您可以像这样轻松地使用自己的静态事件进行测试。
static class MemoryLeak
{
static List<Action<int>> list = new List<Action<int>>();
public static event Action<int> ActivateLeak
{
add
{
list.Add(value);
}
remove
{
list.Remove(value);
}
}
}
然后通过在remove函数中设置断点可以看到你的引用没有被清理。
class Program
{
static void Main(string[] args)
{
foo f = new foo();
MemoryLeak.ActivateLeak += o => f.bar();
f.tryCleanup();
}
}
class foo
{
public void bar()
{ }
public void tryCleanup()
{
MemoryLeak.ActivateLeak -= o => bar();
}
}
作为 Simon 的解决方案的替代方案,您可以使用第二个闭包来创建可以传递的“分离”操作。
foo f = new foo();
Action<int> callfoo = o => f.bar();
MemoryLeak.ActivateLeak += callfoo;
Action cleanUp = () => MemoryLeak.ActivateLeak -= callfoo;
// Now you can pass around the cleanUp action and call it when you need to unsubscribe from the event.
cleanUp();
【讨论】:
这确实是一个阻止垃圾收集的泄漏。有一些方法可以解决它 - WeakReference 是更好的方法之一。
This link 为您提供了很好的对话和很好的答案。
【讨论】: