2024-09-20 10:00:04

Finalize vs Dispose

为什么有些人使用Finalize方法而不是Dispose方法?

在什么情况下你会使用Finalize方法而不是Dispose方法,反之亦然?


当前回答

其他人已经介绍了Dispose和Finalize之间的区别(顺便说一下,Finalize方法在语言规范中仍然被称为析构函数),所以我只补充一点Finalize方法派上用场的场景。

有些类型以一种易于在单个操作中使用和处理的方式封装一次性资源。一般用法通常是这样的:打开,读或写,关闭(Dispose)。它非常适合使用结构。

其他的稍微难一点。实例的WaitEventHandles不像这样使用,因为它们用于从一个线程发送信号到另一个线程。那么问题就变成了谁应该调用Dispose ?作为一种保护措施,这类类型实现了Finalize方法,该方法确保当应用程序不再引用实例时释放资源。

其他回答

Class instances often encapsulate control over resources that are not managed by the runtime, such as window handles (HWND), database connections, and so on. Therefore, you should provide both an explicit and an implicit way to free those resources. Provide implicit control by implementing the protected Finalize Method on an object (destructor syntax in C# and the Managed Extensions for C++). The garbage collector calls this method at some point after there are no longer any valid references to the object. In some cases, you might want to provide programmers using an object with the ability to explicitly release these external resources before the garbage collector frees the object. If an external resource is scarce or expensive, better performance can be achieved if the programmer explicitly releases resources when they are no longer being used. To provide explicit control, implement the Dispose method provided by the IDisposable Interface. The consumer of the object should call this method when it is done using the object. Dispose can be called even if other references to the object are alive.

注意,即使通过Dispose方法提供显式控制,也应该使用Finalize方法提供隐式清理。Finalize提供了一个备份,以防止程序员在调用Dispose失败时永久地泄漏资源。

终结器用于隐式清理——当一个类管理着绝对必须清理的资源时,你应该使用它,否则你会泄漏句柄/内存等等……

正确实现终结器是出了名的困难,应该尽可能避免——SafeHandle类(在. net v2.0及以上版本中可用)现在意味着您很少(如果有的话)需要实现终结器了。

IDisposable接口用于显式清理,并且使用得更普遍——您应该使用它来允许用户在使用完对象后显式地释放或清理资源。

注意,如果您有终结器,那么您还应该实现IDisposable接口,以允许用户显式地更快地释放这些资源,而不是在对象被垃圾收集的情况下。

请参阅DG更新:处置,终结,和资源管理,我认为是最好的和最完整的一组关于终结器和IDisposable的建议。

这是我知道的最好的例子。

 public abstract class DisposableType: IDisposable
  {
    bool disposed = false;

    ~DisposableType()
    {
      if (!disposed) 
      {
        disposed = true;
        Dispose(false);
      }
    }

    public void Dispose()
    {
      if (!disposed) 
      {
        disposed = true;
        Dispose(true);
        GC.SuppressFinalize(this);
      }
    }

    public void Close()
    {
      Dispose();
    }

    protected virtual void Dispose(bool disposing)
    {
      if (disposing) 
      {
        // managed objects
      }
      // unmanaged objects and resources
    }
  }

Finalize是backstop方法,由垃圾回收器在回收对象时调用。Dispose是“确定性清理”方法,应用程序调用它来释放有价值的本机资源(窗口句柄、数据库连接等),当它们不再需要时,而不是让它们无限期地保留,直到GC到达对象。

作为对象的用户,您总是使用Dispose。Finalize是针对GC的。

As the implementer of a class, if you hold managed resources that ought to be disposed, you implement Dispose. If you hold native resources, you implement both Dispose and Finalize, and both call a common method that releases the native resources. These idioms are typically combined through a private Dispose(bool disposing) method, which Dispose calls with true, and Finalize calls with false. This method always frees native resources, then checks the disposing parameter, and if it is true it disposes managed resources and calls GC.SuppressFinalize.

我今天一直在寻找这个问题的答案。我将在这里分享我的经验。我的答案是基于这个联系,因为它有我所见过的最清楚的解释。

当您的对象访问非托管资源时,您必须手动释放这些资源。这可以通过IDisposable或finalizer来完成,这意味着它们都会释放非托管资源。

经验法则: 实现IDisposable以释放非托管资源,调用方代码必须调用Dispose方法。如果调用者忘记调用Dispose()方法,您仍然可以提供一个方法来释放那些非托管资源。第一个选择是使用安全句柄来包装非托管资源。第二个选项是定义终结器。在这种情况下,建议使用安全手柄。

我认为这种联系是对这个问题最明确的答案。我不知道为什么人们在网上对这个问题给出复杂的解释。这让我很困惑,直到我找到了那个联系。