如果我创建一个.NET类,用这样的匿名函数订阅一个事件:
void MyMethod()
{
Application.Current.Deactivated += (s,e) => { ChangeAppearance(); };
}
这个事件处理程序是否会使我的类不被垃圾收集?
如果没有,喔!但如果是这样,你能告诉我删除语法吗?只使用 - =使用相同的代码似乎是错误的。
答案 0 :(得分:3)
我认为你确实需要取消订阅,因为事件提供者(应用程序)比你的消费者更长寿 - 或者至少可以活着。因此,每个订阅实例在应用程序仍然存在时死亡将会造成内存泄漏。
您正在订阅该活动的匿名代表。 这就是为什么你不能以同样的方式取消订阅,因为你不能再解决它了。 实际上,您在订阅的同时创建了该方法,并且不存储任何指向新创建方法的指针。
如果您稍微更改实施以使用“真实”方法,您可以像订阅它一样轻松取消订阅活动:
Application.Current.Deactivated += ChangeAppearance;
Application.Current.Deactivated -= ChangeAppearance;
private void ChangeAppearance(object sender, EventArgs eventArgs)
{
throw new NotImplementedException();
}
答案 1 :(得分:3)
你可以使用Simon D.建议的“真实”方法,或者这样做:
EventHandler handler = null;
handler = (s,e) => {
Application.Current.Deactivated -= handler;
ChangeAppearance();
};
Application.Current.Deactivated += handler;
由于这有点难看并且无法使用lambda作为succint的目的,我可能会将其重构为方法。但了解其他有用的方法很有用。
警告:毋庸置疑,您必须非常小心,不要在订阅活动的点和点之间混淆handler
的值实际调用它的地方,否则取消订阅部分将无法正常工作。
答案 2 :(得分:2)
你一定要清理参考资料。如果有任何疑问,您可以轻松地使用自己的静态事件进行测试。
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();
答案 3 :(得分:1)
这确实是防止垃圾收集的泄漏。有很多方法 - WeakReference是最好的之一。
This link为您提供了良好的对话和良好的答案。