当我们没有析构函数时,为什么要调用SuppressFinalize

som*_*raj 27 .net c# garbage-collection finalizer suppressfinalize

我有几个问题,我无法得到正确的答案.

1)当我们没有析构函数时,为什么我们应该在Dispose函数中调用SuppressFinalize.

2)Dispose和finalize用于在对象被垃圾收集之前释放资源.无论是托管资源还是非托管资源我们都需要释放它,那么为什么我们需要在dispose函数中使用一个条件,当我们从IDisposable调用这个重写函数时传递'true':从finalize调用时Dispose并传递false.

请参阅我从网上复制的以下代码.

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函数并实现如下所示.

   class Test : IDisposable
   {
     private bool isDisposed = false;

     ~Test()
     {
       Dispose();
     }


     public void Dispose()
     {
      // Code to dispose the managed resources of the class
      // Code to dispose the un-managed resources of the class
      isDisposed = true;

      // Call this since we have a destructor . what if , if we don't have one 
       GC.SuppressFinalize(this);
     }
   }       
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 24

我的肢体会在这里,但是......大多数人并不需要全面的Dispose模式.它的设计是为了能够直接访问非托管资源(通常是通过IntPtr)并面向继承.大多数情况下,这些都不是实际需要的.

如果您只是持有对其他实现的东西的引用IDisposable,那么您几乎肯定不需要终结器 - 无论是什么保存资源都直接负责处理它.你可以做这样的事情:

public sealed class Foo : IDisposable
{
    private bool disposed;
    private FileStream stream;

    // Other code

    public void Dispose()
    {
        if (disposed)
        {
            return;
        }
        stream.Dispose();
        disposed = true;
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,这不是线程安全的,但这可能不会成为问题.

通过不必担心子类直接保存资源的可能性,您不需要抑制终结器(因为没有终结器) - 并且您也不需要提供自定义处理的子类方法.没有继承,生活就更简单了.

如果你确实需要允许不受控制的继承(即你不愿意打赌子类将有非常特殊的需求),那么你需要寻找完整的模式.

请注意,使用SafeHandle.NET 2.0,您需要自己的终结器甚至比在.NET 1.1中更少.


为了解决为什么首先有一个disposing标志的问题:如果你在终结器中运行,你引用的其他对象可能已经完成.你应该让他们自己清理,你应该只清理你直接拥有的资源.

  • 我要走的远远超过"大多数人不需要全面的处置模式":没有人需要它,因为将需要清理的托管和非托管资源直接作为字段保存为反模式,而"处理模式"是处理一个人有这种情况的聪明方式,这使得它成为处理你做了一些愚蠢事实的聪明方法.任何人都应该使用它的唯一时间是通过继承某些东西来强迫它们. (3认同)

Dea*_*alk 6

以下是主要事实

1) Object.Finalize 是您的类在具有终结器时覆盖的内容。~TypeName() 析构函数方法只是“覆盖 Finalize()”等的简写

2)如果您在完成之前(即从 using 块等中出来时)在 Dispose 方法中处理资源,则调用 GC.SuppressFinalize。如果您没有终结器,则无需执行此操作。如果你有一个终结器,这可以确保对象从终结队列中取出(所以我们不会两次处理东西,因为终结器通常也会调用 Dispose 方法)

3)您将终结器实现为“故障安全”机制。终结器保证运行(只要 CLR 没有中止),因此它们允许您确保在未调用 Dispose 方法的情况下清理代码(可能程序员忘记在“使用”中创建实例)块等

4) 终结器很昂贵,因为具有终结器的类型不能在第 0 代集合(最有效)中进行垃圾回收,并通过 F-Reachable 队列中对它们的引用提升到第 1 代,因此它们代表一个GC 根。直到 GC 执行第 1 代收集,才会调用终结器,并释放资源 - 因此仅在非常重要时才实现终结器 - 并确保需要终结的对象尽可能小 - 因为所有可以您的可终结对象到达的对象也将被提升到第 1 代。


Ode*_*ded 5

保留第一个版本,它更安全,是配置模式的正确实现.

  1. 调用SuppressFinalize告诉GC你已经完成了所有的破坏/处理(你的类所拥有的资源)并且它不需要调用析构函数.

  2. 你需要测试的情况下,使用您的类的代码已经已经叫处置,你不应该告诉GC重新配置.

请参阅 MSDN文档(Dispose方法应调用SuppressFinalize).

  • @somaraj:关键是*你的*类可能没有终结器,但*子类*可能. (5认同)
  • 该规则仅在您需要终结器或您需要允许子类具有终结器时才相关.在许多情况下,情况并非如此. (2认同)