【问题标题】:Do I need to remove this sort of event handler?我需要删除这种事件处理程序吗?
【发布时间】:2011-03-04 19:47:39
【问题描述】:

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

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

此事件处理程序是否会成为防止我的班级被垃圾收集的根?

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

【问题讨论】:

    标签: c# .net delegates


    【解决方案1】:

    您可以使用 Simon D. 建议的“真实”方法,也可以这样做:

    EventHandler handler = null;
    handler = (s,e) => { 
        Application.Current.Deactivated -= handler;
        ChangeAppearance();
    };
    Application.Current.Deactivated += handler;
    

    由于这有点丑陋并且违背了使用 lambda 简洁的目的,我可能会将其重构为一个方法。但了解其他方法很有用。

    警告:不言而喻,您必须非常小心,不要在订阅事件的时间点和订阅事件的时间点之间弄乱handler 的值实际调用,否则取消订阅部分将无法正常工作。

    【讨论】:

    • 我喜欢这里简洁的风格。根据我的需要,这应该可以完美地工作。感谢您花时间写这篇文章!
    【解决方案2】:

    我认为您确实需要取消订阅,因为事件提供者(应用程序)的生存时间(或至少可以生存)比您的消费者更长。因此,当应用程序仍然存在时,每个订阅实例都会死去,这会造成内存泄漏。

    您正在为该事件订阅一个匿名代表。 这就是为什么你不能以同样的方式取消订阅它,因为你不能再解决它了。 实际上,您是在订阅的同时创建方法,并且您没有存储任何指向新创建方法的指针。

    如果您稍微更改您的实现以使用“真实”方法,您可以像订阅它一样轻松地取消订阅该事件:

    Application.Current.Deactivated += ChangeAppearance;
    Application.Current.Deactivated -= ChangeAppearance;
    private void ChangeAppearance(object sender, EventArgs eventArgs)
    {
        throw new NotImplementedException();
    }
    

    【讨论】:

    • 感谢您的怀疑。 -约翰
    【解决方案3】:

    你一定要清理参考。如果有任何疑问,您可以像这样轻松地使用自己的静态事件进行测试。

    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();
    

    【讨论】:

      【解决方案4】:

      这确实是一个阻止垃圾收集的泄漏。有一些方法可以解决它 - WeakReference 是更好的方法之一。

      This link 为您提供了很好的对话和很好的答案。

      【讨论】:

        猜你喜欢
        • 2011-09-26
        • 2010-11-21
        • 1970-01-01
        • 1970-01-01
        • 2011-01-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多