c# - 事件如何导致 C# 中的内存泄漏以及弱引用如何帮助缓解这种情况?

标签 c# .net events weak-references

有两种方式(据我所知)会导致 C# 中的意外内存泄漏:

  1. 不释放实现IDisposable的资源
  2. 错误地引用和取消引用事件。

第二点我不是很明白。如果源对象的生命周期比监听器长,并且监听器在没有其他引用时不再需要事件,则使用普通的 .NET 事件会导致内存泄漏:源对象在内存中保存监听器对象应该被垃圾收集。

您能否解释一下事件如何导致 C# 中的代码发生内存泄漏,以及我如何使用弱引用和不使用弱引用来编写代码来绕过它?

最佳答案

当监听器将事件监听器附加到事件时,源对象将获得对监听器对象的引用。这意味着在分离事件处理程序或收集源对象之前,垃圾收集器无法收集监听器。

考虑以下类:

class Source
{
    public event EventHandler SomeEvent;
}

class Listener
{
    public Listener(Source source)
    {
        // attach an event listner; this adds a reference to the
        // source_SomeEvent method in this instance to the invocation list
        // of SomeEvent in source
        source.SomeEvent += new EventHandler(source_SomeEvent);
    }

    void source_SomeEvent(object sender, EventArgs e)
    {
        // whatever
    }
}

...然后是以下代码:

Source newSource = new Source();
Listener listener = new Listener(newSource);
listener = null;

即使我们将 null 分配给 listener,它也不符合垃圾回收的条件,因为 newSource 仍然持有对事件处理程序(Listener.source_SomeEvent)。要修复此类泄漏,重要的是在不再需要时始终分离事件监听器。

上面的示例是为了关注泄漏问题而编写的。为了修复该代码,最简单的方法可能是让 Listener 保留对 Source 的引用,以便稍后可以分离事件监听器:

class Listener
{
    private Source _source;
    public Listener(Source source)
    {
        _source = source;
        // attach an event listner; this adds a reference to the
        // source_SomeEvent method in this instance to the invocation list
        // of SomeEvent in source
        _source.SomeEvent += source_SomeEvent;
    }

    void source_SomeEvent(object sender, EventArgs e)
    {
        // whatever
    }

    public void Close()
    {
        if (_source != null)
        {
            // detach event handler
            _source.SomeEvent -= source_SomeEvent;
            _source = null;
        }
    }
}

然后调用代码可以发出信号表明它已使用该对象完成,这将删除 Source 对 ´Listener` 的引用;

Source newSource = new Source();
Listener listener = new Listener(newSource);
// use listener
listener.Close();
listener = null;

关于c# - 事件如何导致 C# 中的内存泄漏以及弱引用如何帮助缓解这种情况?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3662842/

相关文章:

javascript - Jquery:似乎无法获取选择标签更改

c# - 如何在 core2.1 中使用 WinHttpHandler 和 IHttpClientFactory?

c# - 随机生成敌人 XNA 4.0 (WP7)

c# - 如何使用 unity + UnityAutoRegistration 进行开放式通用装饰器链接

c# - 覆盖方法是否被视为虚拟方法?

c# - 如何制作 win 表单应用程序设置的副本?

linux - Linux 文件描述符多路复用策略(select 等)

Laravel 架构 - 事件与 Controller

c# - 以编程方式添加多个扩展器事件将不起作用

c# - 快速图中的无向图表示