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/

相关文章:

c# - PCL .NET 4.5 计时器

c# - 您如何将外部库打包到您的 .Net 项目中?

.net - 哪个版本的 .NET 可用于 Xbox 360?

javascript - 如果在 DOM 中的任何地方单击,则隐藏 div

c# - WPF slider ValueChanged 事件的频率

android - iOS 中是否有类似 BroadcastReceivers 的东西?

c# - 如何将视频的第一帧保存为图像?

c# - 任何用于 Visual Studio 的错误跟踪插件?

c# - 在 MVVM 中对 ViewModel 进行批量更改时,如何使用批处理,例如 BeginUpdate/EndUpdate?

c# - DebuggerStepThrough 属性 - 如何也跳过子方法