完成vs Dispose

时间:2009-04-09 05:00:43

标签: c# dispose

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

在什么情况下,您会在Finalize方法上使用Dispose方法,反之亦然?

15 个答案:

答案 0 :(得分:125)

当您的对象被垃圾收集时调用终结器方法,并且您无法保证何时会发生这种情况(您可以强制它,但这会损害性能)。

另一方面,Dispose方法由创建类的代码调用,以便您可以清理和释放已获取的任何资源(非托管数据,数据库连接,文件句柄等) )代码完成对象的那一刻。

标准做法是实施IDisposableDispose,以便您可以在using声明中使用您的对象。例如using(var foo = new MyObject()) { }。在你的终结者中,你打电话给Dispose,以防呼叫代码忘记丢弃你。

答案 1 :(得分:107)

其他人已经介绍了DisposeFinalize之间的区别(顺便说一句Finalize方法在语言规范中仍被称为析构函数),所以我只想补充一点<{1}}方法派上用场的方案。

某些类型以易于使用的方式封装一次性资源,并在一次操作中处理它们。一般用法通常是这样的:打开,读取或写入,关闭(Dispose)。它非常适合Finalize构造。

其他人有点困难。实例的using不是这样使用的,因为它们用于从一个线程发信号到另一个线程。那么问题就是谁应该对这些问题致电WaitEventHandles?像这样的安全措施类型实现了Dispose方法,这可以确保在应用程序不再引用实例时处理资源。

答案 2 :(得分:62)

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

作为对象的用户,您始终使用Dispose。最终确定是GC。

作为类的实现者,如果您拥有应该处理的托管资源,则实现Dispose。如果您拥有本机资源,则实现Dispose和Finalize,并且都调用释放本机资源的公共方法。这些习语通常通过私有Dispose(bool disposing)方法组合,Dispose调用为true,Finalize调用为false。此方法始终释放本机资源,然后检查disposing参数,如果为true,则处置托管资源并调用GC.SuppressFinalize。

答案 3 :(得分:39)

<强>的Finalize

  • 终结器应始终为protected,而不是publicprivate,因此无法直接从应用程序的代码调用该方法,同时,它可以调用base.Finalize方法
  • 终结者应该只发布非托管资源。
  • 框架不保证终结器在任何给定的实例上都会执行。
  • 永远不要在终结器中分配内存或从终结器调用虚拟方法。
  • 避免同步并在终结器中引发未处理的异常。
  • 终结器的执行顺序是非确定性的 - 换句话说,你不能依赖终结器中仍然可用的另一个对象。
  • 不要在值类型上定义终结器。
  • 不要创建空的析构函数。换句话说,除非你的类需要清理非托管资源,否则你永远不应该显式定义析构函数,如果你确实定义了一个,它应该做一些工作。如果稍后您不再需要清除析构函数中的非托管资源,请将其完全删除。

<强>处置

  • 在每个具有终结工具的类型上实施IDisposable
  • 在调用Dispose方法后,确保对象无法使用。换句话说,避免在调用Dispose方法后使用对象。
  • 完成所有Dispose类型后,请致电IDisposable
  • 允许多次调用Dispose而不会引发错误。
  • 使用Dispose方法
  • 禁止在GC.SuppressFinalize方法中调用终结器
  • 避免创建一次性价值类型
  • 避免在Dispose方法
  • 中抛出异常

Dispose / Finalized Pattern

  • Microsoft建议您在使用非托管资源时同时实施DisposeFinalize。即使开发人员忽略了明确调用Finalize方法,也会运行Dispose实现,并且在对象被垃圾回收时仍会释放资源。
  • 清除Finalize方法中的非托管资源以及Dispose方法。另外,使用Dispose方法将Dispose方法作为该类中的组件(具有非托管资源作为其成员)的任何.NET对象调用。

答案 4 :(得分:27)

当此对象不再使用时,GC会调用Finalize。

Dispose只是一个普通的方法,这个类的用户可以调用它来释放任何资源。

如果用户忘记调用Dispose,并且该类已实现Finalize,那么GC将确保调用它。

答案 5 :(得分:16)

有关MCSD认证工具包(考试70-483)第193页的一些关键词:

析构函数≈(它几乎等于)base.Finalize(),析构函数被转换为Finalize方法的覆盖版本,该方法执行析构函数的代码然后调用基础class的Finalize方法。然后,它完全不确定,你能够知道什么时候被调用,因为它取决于GC。

如果某个类不包含托管资源且没有非托管资源,则不需要 实现IDisposableor有一个析构函数。

如果该类只有托管资源,它应该实现IDisposable,但它不会 需要一个析构函数。 (当析构函数执行时,您仍然无法确定托管对象 存在,所以无论如何你都不能调用他们的Dispose方法。)

如果该类只有非托管资源,则需要实现IDisposable并且需要 程序不调用Dispose时的析构函数。

Dispose方法必须安全才能运行多次。你可以通过使用a来实现 变量以跟踪它之前是否已经运行过。

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);
    }

    // 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;

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

答案 6 :(得分:7)

99%的时间,你不必担心。 :)但是,如果您的对象包含对非托管资源(例如窗口句柄,文件句柄)的引用,则需要为托管对象提供释放这些资源的方法。 Finalize给出了释放资源的隐式控制。它由垃圾收集器调用。 Dispose是一种明确控制资源释放的方法,可以直接调用。

关于Garbage Collection的主题还有很多东西需要学习,但这是一个开始。

答案 7 :(得分:5)

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

正确地实现终结器是非常困难的并且应该尽可能避免 - SafeHandle类(在.Net v2.0及更高版本中)现在意味着您很少(如果有的话)需要实现终结器更多。

IDisposable接口用于显式清理并且更常用 - 您应该使用它来允许用户在完成使用对象时显式释放或清理资源。

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

请参阅DG Update: Dispose, Finalization, and Resource Management,我认为这是有关终结工具和IDisposable的最佳和最完整的建议。

答案 8 :(得分:2)

摘要是-

  • 如果您的课程引用了非托管类,则可以为其编写终结器 资源,并且您要确保这些非托管资源 当该类的实例被垃圾回收时被释放 自动。请注意,您不能显式调用对象的终结器-垃圾收集器会在必要时自动调用该终结器。
  • 另一方面,您实现IDisposable接口(和 因此,当您的班级定义Dispose()方法作为您班级的结果) 引用了非托管资源,但您不想等待 要启动的垃圾收集器(可以随时-不在 程序员的控制),并希望将这些资源释放为 完成后尽快。因此,您可以通过调用对象的Dispose()方法来明确地释放非托管资源。

另外,另一个区别是-在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);
     }

答案 9 :(得分:1)

我知道的最好的例子。

 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
    }
  }

答案 10 :(得分:1)

C#中Finalize和Dispose方法之间的差异。

GC调用finalize方法来回收非托管资源(例如文件操作,windows api,网络连接,数据库连接),但GC调用它时不会修复时间。它被GC隐式调用,这意味着我们对它没有低级控制。

Dispose方法:当我们从代码中调用它时,我们对它进行低级控制。每当我们觉得它不可用时,我们就可以回收非托管资源。我们可以通过实现IDisposal模式来实现这一点。

答案 11 :(得分:1)

类实例通常封装对未由运行时管理的资源的控制,例如窗口句柄(HWND),数据库连接等。因此,您应该提供一种显式和隐式方式来释放这些资源。通过在对象上实现受保护的Finalize方法(C#中的析构函数语法和C ++的托管扩展)来提供隐式控制。垃圾收集器在不再有对该对象的任何有效引用之后的某个时刻调用此方法。 在某些情况下,您可能希望为使用对象的程序员提供在垃圾收集器释放对象之前显式释放这些外部资源的能力。如果外部资源稀缺或昂贵,如果程序员在不再使用资源时明确释放资源,则可以实现更好的性能。要提供显式控制,请实现IDisposable接口提供的Dispose方法。对象的使用者应该在使用该对象完成后调用此方法。即使对象的其他引用仍然存在,也可以调用Dispose。

请注意,即使通过Dispose提供显式控制,也应使用Finalize方法提供隐式清理。如果程序员无法调用Dispose,则Finalize提供备份以防止资源永久泄漏。

答案 12 :(得分:1)

Dispose和Finalize之间的主要区别在于:

Dispose通常由您的代码调用。调用资源后,资源立即释放。人们忘记了调用该方法,因此发明了using() {}语句。当程序完成{}内的代码执行后,它将自动调用Dispose方法。

您的代码未调用

Finalize。它是由垃圾收集器(GC)调用的。这意味着将来GC可以决定释放资源。当GC工作时,它将经历许多Finalize方法。如果您对此有沉重的逻辑,则会使过程变慢。它可能会导致程序性能问题。因此,请注意放入其中的内容。

我个人会在Dispose中编写大部分销毁逻辑。希望这可以消除混乱。

答案 13 :(得分:-1)

我们知道dispose和finalize都用于释放非托管资源.. 但不同之处是finalize使用两个循环来释放资源,其中dispose使用一个循环。

答案 14 :(得分:-1)

要在第一部分回答,您应该提供人们使用的示例 完全相同的类对象的不同方法。 否则,很难(甚至很奇怪)回答。

关于第二个问题,最好先阅读 Proper use of the IDisposable interface 声称

  

这是您的选择!但是选择“处置”。

换句话说:GC仅知道终结器(如果有的话。也称为Microsoft的析构函数)。 好的代码将尝试从finalizer和Dispose两者中清除。