Dispose() 用于清理托管资源?

Posted

技术标签:

【中文标题】Dispose() 用于清理托管资源?【英文标题】:Dispose() for cleaning up managed resources? 【发布时间】:2013-05-12 04:50:46 【问题描述】:

在this answer我找到了,

清理 Finalize 方法中的非托管资源和 Dispose 方法中的托管对象,当 Dispose/Finalize 模式时 已在您的代码中使用。

后来我发现 this nice article 关于 finalize 和 dispose 并对它们有了一个清晰的认识。文章有如下代码(第3页),解释概念:

class Test : IDisposable

    private bool isDisposed = false;

    ~Test()
    
       Dispose(false);
    

    protected void Dispose(bool disposing)
    
       if (disposing)
       
          // Code to dispose the managed resources of the class
       
       // Code to dispose the un-managed resources of the class

       isDisposed = true;
    

    public void Dispose()
    
       Dispose(true);
       GC.SuppressFinalize(this);
    

但在此之下,会出现相同的注释(我在此问题的开头包含)。

Dispose/Finalize 模式 Microsoft 建议您在使用非托管资源时同时实现 Dispose 和 Finalize。那么正确的顺序将 供开发人员调用 Dispose。 Finalize 实施将 运行并且当对象被释放时资源仍然会被释放 即使开发人员忽略调用 Dispose 也会收集垃圾 明确的方法。 Francesco Balena 在他的博客中写道“ 仅当您的类型调用时才应使用 Dispose/Finalize 模式 分配非托管资源的非托管代码(包括非托管 memory) 并返回一个句柄,您最终必须使用它来释放 资源。 dispose 和 finalize 都必须链接到它们的父级 对象通过调用其父对象各自的方法 处置或确定了自己的成员”。 简单来说就是在 Finalize 方法中清理非托管资源,在 Dispose 方法中清理托管资源,当 您的代码中使用了 Dispose/Finalize 模式。

现在我又糊涂了。在整篇文章和代码示例中,都表明应该在Dispose() 中释放非托管资源。但是那条评论的相关性是什么?

编辑:

由于已确认这一行:

简单地说,在 Finalize 方法中清理非托管资源并 Dispose 方法中的托管对象,当 Dispose/Finalize 模式已在您的代码中使用

是错误的,我编辑了this answer。

【问题讨论】:

【参考方案1】:

看它非常简单。

    如果您正在处理非托管资源 - 同时实现 DisposeFinalizeDispose 将被开发人员调用以释放资源,只要他们看到它不再需要它们。如果他们忘记调用Dispose,那么框架会在自己的 GC 周期中调用 finalize(通常会花费自己的时间)。 如果您的对象在内部使用一次性对象 - 如果您创建并保留了对实现 Dispose() 且您尚未处置的类型的任何对象的引用,则您实现了 Dispose()如果以上都不是这种情况(您不是在处理非托管资源,也不是您的对象在内部使用一次性对象) - 然后不要做任何事情。不要实现FinalizeDispose

一些经典的例子:

System.IO.FileStream 对象管理文件的锁/流句柄。所以它同时实现了 dispose 和 finalize。如果开发人员处理了它,那么其他程序可以立即访问它。如果他忘记释放它,那么框架会完成它并在其 GC 周期的后期关闭句柄。

System.Text.StringBuilder 没有任何非托管资源。所以没有 dispose 没有 finalize。

就模式而言,它意味着什么

// Code to dispose the managed resources of the class

是调用您在该类中作为组件拥有的任何 .NET 对象的 Dispose 方法

还有

// Code to dispose the un-managed resources of the class

表示关闭原始句柄和指针。这是带有示例的更新代码

class Test : IDisposable

  private bool isDisposed = false;

  ~Test()
  
    Dispose(false);
  

  protected void Dispose(bool disposing)
  
    if (!isDisposed)
    
      if (disposing)
      
        // Code to dispose the managed resources of the class
        internalComponent1.Dispose();
        internalComponent2.Dispose();
      

      // Code to dispose the un-managed resources of the class
      CloseHandle(handle);
      handle = IntPtr.Zero;   

      isDisposed = true;
    
  

  public void Dispose()
  
    Dispose(true);
    GC.SuppressFinalize(this);
  

Here is an old question解释一下

【讨论】:

实际上根本没有清理托管资源。我不认为他的观点有任何相关性。清理托管资源最多可以将其分配为 NULL。内置这么好的 GC 是完全没有必要的。 那是 2006 年写的文章,其实我不会太在意。 没那么简单。如果您创建并保留了对实现 Dispose() 且您尚未处置的类型的 any 对象的引用,您还应该实现 Dispose()。有些类型不使用非托管资源,但它们确实具有诸如订阅事件之类的东西,它们必须在其Dispose() 方法中取消挂钩。即使它们不使用非托管资源,您也必须处理这些对象,否则您可能会出现某种内存泄漏。对于派生自 ControlUserControl 的 UI 类,这通常尤其如此。 为什么 Finalize 方法不释放托管资源?如果从未调用过 Dipose 方法,谁来释放托管资源? GC 会看到资源(托管)没有指向它的指针并清理它【参考方案2】:

如果Foo 拥有可以从确定性清理中受益的资源,但没有可以在终结器中有效清理的资源,则它应实现IDisposable,但不应覆盖Finalize 或具有析构函数。如果一个类拥有多个资源,并且至少一个可以在终结器中清理,那么每个可以在终结器中清理的离散资源都应该封装到它自己的配备终结器/析构器的对象中(可以在受保护的嵌套类),并且包含这些资源的类应该包含对包装器对象的引用。完成后,外部类将适合具有 Dispose 方法但没有终结器/析构函数的类的模式。

【讨论】:

以上是关于Dispose() 用于清理托管资源?的主要内容,如果未能解决你的问题,请参考以下文章

利用using和try/finally语句来清理资源

在哪里清理处置模式中的非托管资源?

C#学习笔记---Dispose(),Finalize(),SuppressFinalize

自动内存管理 21.8-21.21

垃圾回收

C#-C#中的Dispose模式