2024-09-20 10:00:04

Finalize vs Dispose

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

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


当前回答

以下是MCSD认证工具包(考试70-483)第193页中的一些关键信息:

析构函数≈(几乎等于)base.Finalize(),析构函数被转换为Finalize方法的重写版本,该方法执行析构函数的代码,然后调用基类的Finalize方法。这是完全不确定的,你不知道什么时候会被调用,因为这取决于GC。

如果一个类不包含托管资源和非托管资源,它就不应该实现IDisposable或具有析构函数。

如果类只有托管资源,它应该实现IDisposable,但不应该有析构函数。(当析构函数执行时,您仍然不能确定托管对象 所以你不能调用它们的Dispose()方法。)

如果类只有非托管资源,则需要实现IDisposable,并需要析构函数以防程序不调用Dispose()。

Dispose()方法必须能够安全地运行多次。您可以通过使用一个变量来跟踪它以前是否运行过来实现这一点。

Dispose()应该同时释放托管和非托管资源。

析构函数应该只释放非托管资源。当析构函数执行时,您 不能确定托管对象是否仍然存在,因此无论如何都不能调用它们的Dispose方法。这是通过使用规范的受保护的void Dispose(bool Dispose)模式获得的,其中只有在Dispose == true时才释放(Dispose)托管资源。

释放资源后,Dispose()应该调用GC。SuppressFinalize,这样对象就可以 跳过结束队列。

一个具有非托管资源和托管资源的类的实现示例:

using System;

class DisposableClass : IDisposable
{
    // A name to keep track of the object.
    public string Name = "";

    // Free managed and unmanaged resources.
    public void Dispose()
    {
        FreeResources(true);

        // We don't need the destructor because
        // our resources are already freed.
        GC.SuppressFinalize(this);
    }

    // Destructor to clean up unmanaged resources
    // but not managed resources.
    ~DisposableClass()
    {
        FreeResources(false);
    }

    // Keep track if whether resources are already freed.
    private bool ResourcesAreFreed = false;

    // Free resources.
    private void FreeResources(bool freeManagedResources)
    {
        Console.WriteLine(Name + ": FreeResources");
        if (!ResourcesAreFreed)
        {
            // Dispose of managed resources if appropriate.
            if (freeManagedResources)
            {
                // Dispose of managed resources here.
                Console.WriteLine(Name + ": Dispose of managed resources");
            }

            // Dispose of unmanaged resources here.
            Console.WriteLine(Name + ": Dispose of unmanaged resources");

            // Remember that we have disposed of resources.
            ResourcesAreFreed = true;
        }
    }
}

其他回答

以下是MCSD认证工具包(考试70-483)第193页中的一些关键信息:

析构函数≈(几乎等于)base.Finalize(),析构函数被转换为Finalize方法的重写版本,该方法执行析构函数的代码,然后调用基类的Finalize方法。这是完全不确定的,你不知道什么时候会被调用,因为这取决于GC。

如果一个类不包含托管资源和非托管资源,它就不应该实现IDisposable或具有析构函数。

如果类只有托管资源,它应该实现IDisposable,但不应该有析构函数。(当析构函数执行时,您仍然不能确定托管对象 所以你不能调用它们的Dispose()方法。)

如果类只有非托管资源,则需要实现IDisposable,并需要析构函数以防程序不调用Dispose()。

Dispose()方法必须能够安全地运行多次。您可以通过使用一个变量来跟踪它以前是否运行过来实现这一点。

Dispose()应该同时释放托管和非托管资源。

析构函数应该只释放非托管资源。当析构函数执行时,您 不能确定托管对象是否仍然存在,因此无论如何都不能调用它们的Dispose方法。这是通过使用规范的受保护的void Dispose(bool Dispose)模式获得的,其中只有在Dispose == true时才释放(Dispose)托管资源。

释放资源后,Dispose()应该调用GC。SuppressFinalize,这样对象就可以 跳过结束队列。

一个具有非托管资源和托管资源的类的实现示例:

using System;

class DisposableClass : IDisposable
{
    // A name to keep track of the object.
    public string Name = "";

    // Free managed and unmanaged resources.
    public void Dispose()
    {
        FreeResources(true);

        // We don't need the destructor because
        // our resources are already freed.
        GC.SuppressFinalize(this);
    }

    // Destructor to clean up unmanaged resources
    // but not managed resources.
    ~DisposableClass()
    {
        FreeResources(false);
    }

    // Keep track if whether resources are already freed.
    private bool ResourcesAreFreed = false;

    // Free resources.
    private void FreeResources(bool freeManagedResources)
    {
        Console.WriteLine(Name + ": FreeResources");
        if (!ResourcesAreFreed)
        {
            // Dispose of managed resources if appropriate.
            if (freeManagedResources)
            {
                // Dispose of managed resources here.
                Console.WriteLine(Name + ": Dispose of managed resources");
            }

            // Dispose of unmanaged resources here.
            Console.WriteLine(Name + ": Dispose of unmanaged resources");

            // Remember that we have disposed of resources.
            ResourcesAreFreed = true;
        }
    }
}

摘要如下-

You write a finalizer for your class if it has reference to unmanaged resources and you want to make sure that those unmanaged resources are released when an instance of that class is garbage collected automatically. Note that you can't call the Finalizer of an object explicitly - it's called automatically by the garbage collector as and when it deems necessary. On the other hand, you implement the IDisposable interface(and consequently define the Dispose() method as a result for your class) when your class has reference to unmanaged resources, but you don't want to wait for the garbage collector to kick in (which can be anytime - not in control of the programmer) and want to release those resources as soon as you are done. Thus, you can explicitly release unmanaged resources by calling an object's Dispose() method.

另外,另一个区别是——在Dispose()实现中,您也应该释放托管资源,而在Finalizer中不应该这样做。这是因为对象引用的托管资源很可能在准备完成之前就已经被清理了。

对于使用非托管资源的类,最佳实践是同时定义Dispose()方法和Finalizer,以便在开发人员忘记显式地释放对象时作为备用方案使用。两者都可以使用共享的方法来清理托管和非托管资源:-

class ClassWithDisposeAndFinalize : IDisposable
{
    // Used to determine if Dispose() has already been called, so that the finalizer
    // knows if it needs to clean up unmanaged resources.
     private bool disposed = false;

     public void Dispose()
     {
       // Call our shared helper method.
       // Specifying "true" signifies that the object user triggered the cleanup.
          CleanUp(true);

       // Now suppress finalization to make sure that the Finalize method 
       // doesn't attempt to clean up unmanaged resources.
          GC.SuppressFinalize(this);
     }
     private void CleanUp(bool disposing)
     {
        // Be sure we have not already been disposed!
        if (!this.disposed)
        {
             // If disposing equals true i.e. if disposed explicitly, dispose all 
             // managed resources.
            if (disposing)
            {
             // Dispose managed resources.
            }
             // Clean up unmanaged resources here.
        }
        disposed = true;
      }

      // the below is called the destructor or Finalizer
     ~ClassWithDisposeAndFinalize()
     {
        // Call our shared helper method.
        // Specifying "false" signifies that the GC triggered the cleanup.
        CleanUp(false);
     }

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

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

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

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

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

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

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

finalizer方法在对象被垃圾收集时被调用,并且您无法保证何时会发生这种情况(您可以强制执行,但这会影响性能)。

另一方面,Dispose方法应该由创建类的代码调用,以便在代码处理完对象后立即清理和释放已获得的任何资源(非托管数据、数据库连接、文件句柄等)。

标准实践是实现IDisposable和Dispose,这样就可以在using语句中使用对象。例如使用(var foo = new MyObject()){}。在终结器中,你调用Dispose,以防调用代码忘记释放你。