我需要删除这种事件处理程序吗?

时间:2011-03-04 19:47:40

标签: c# .net delegates

如果我创建一个.NET类,用这样的匿名函数订阅一个事件:

void MyMethod()
{
   Application.Current.Deactivated += (s,e) => { ChangeAppearance(); };
}

这个事件处理程序是否会使我的类不被垃圾收集?

如果没有,喔!但如果是这样,你能告诉我删除语法吗?只使用 - =使用相同的代码似乎是错误的。

4 个答案:

答案 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为您提供了良好的对话和良好的答案。