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

Sha*_*run 21 c# dispose finalize

这个答案中我发现,

在代码中使用Dispose/Finalize模式时,清除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);
    }
}
Run Code Online (Sandbox Code Playgroud)

但在此之下,出现了相同的注释(我在本问题开头所包含的注释).

Dispose/Finalize模式Microsoft建议您在使用非托管资源时实现Dispose和Finalize.然后,正确的顺序是开发人员调用Dispose.即使开发人员忽略了显式调用Dispose方法,也会在对象被垃圾回收时运行Finalize实现并仍然释放资源.Francesco Balena在他的博客中写道:只有当你的类型调用分配非托管资源(包括非托管内存)的非托管代码并返回最终必须用来释放资源的句柄时,才应使用Dispose/Finalize模式.只需处理和完成必须通过在处理或完成自己的成员之后调用父母的相应方法来链接到他们的父对象". 简单地说,在代码中使用Dispose/Finalize模式时,清除Finalize方法中的非托管资源和Dispose方法中的托管资源.

现在我又困惑了.在整篇文章和代码示例中,显示应释放非托管资源Dispose().但那个评论的相关性是什么?

编辑:

正如确认这一行:

简单地说,当在代码中使用Dispose/Finalize模式时,清理Finalize方法中的非托管资源和Dispose方法中的托管资源.

是错误的,我编辑了这个答案.

Gur*_*ara 45

看得很简单.

  1. 如果您正在处理非托管资源 - 实现两者DisposeFinalize.Dispose开发人员要求他们在资源被认为不再需要资源时立即释放资源.如果他们忘记调用Dispose那么Framework调用它自己的GC循环中的finalize(通常会花费自己的甜蜜时间).
  2. 如果您不处理非托管资源 - 那么就不要做任何事情.不要实施Dispose()也不要Dispose().
  3. 如果您的对象在内部使用Disposable对象 - Finalize如果您创建并保留了对实现类型的任何对象的引用,Dispose并且您尚未处置该对象,则可以实现.

一些经典的例子:

System.IO.FileStreamobject管理文件的锁/流句柄.所以它实现了dispose和finalize.如果开发人员处理它,那么另一个程序可以立即访问它.如果他忘记处理它,那么Framework最终确定它并在GC循环中关闭句柄.

System.Text.StringBuilder没有任何非托管资源.所以没有处理没有最终确定.

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

// Code to dispose the managed resources of the class
Run Code Online (Sandbox Code Playgroud)

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

// Code to dispose the un-managed resources of the class
Run Code Online (Sandbox Code Playgroud)

意味着关闭原始句柄和指针.这是您的更新代码和示例

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);
  }
}
Run Code Online (Sandbox Code Playgroud)

这是一个解释它的老问题

  • 它不是那么简单.如果您创建并保留了对实现`Dispose()`并且尚未处理的类型的*any*对象的引用,则还应该实现`Dispose()`.有些类型不使用非托管资源,但是它们有诸如订阅事件的东西,它们必须在它们的`Dispose()`方法中取消挂钩.您必须处置此类对象,即使它们不使用非托管资源,否则您可能会出现内存泄漏.对于从"Control"或"UserControl"派生的UI类,通常尤其如此. (5认同)
  • 为什么Finalize方法不释放托管资源?如果从未调用过Dipose方法,谁将释放托管资源? (3认同)
  • GC 会看到资源(托管)没有指向它的指针并清理它 (3认同)
  • `isDisposed`在此代码中未执行任何操作。 (2认同)

sup*_*cat 5

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