Зачем вызывать dispose (false) в деструкторе? - PullRequest
60 голосов
/ 10 марта 2009

Ниже приведен типичный пример шаблона утилизации:

 public bool IsDisposed { get; private set; }

  #region IDisposable Members

  public void Dispose()
  {
    Dispose(true);
    GC.SuppressFinalize(this);
  }

  protected virtual void Dispose(bool disposing)
  {
    if (!IsDisposed)
    {
      if (disposing)
      {
        //perform cleanup here
      }

      IsDisposed = true;
    }
  }

  ~MyObject()
  {
    Dispose(false);
  }

Я понимаю, что делает dispose, но я не понимаю, почему вы захотите вызвать dispose (false) в деструкторе? Если вы посмотрите на определение, оно абсолютно ничего не сделает, так зачем кому-то писать такой код? Разве не имеет смысла просто не вызывать избавиться от деструктора вообще?

Ответы [ 6 ]

43 голосов
/ 10 марта 2009

Финализатор используется в качестве запасного варианта, если объект по какой-либо причине не расположен должным образом. Обычно вызывается метод Dispose(), который удаляет подключение финализатора и превращает объект в обычный управляемый объект, который сборщик мусора легко может удалить.

Вот пример из MSDN класса, который управляет и неуправляет ресурсами для очистки.

Обратите внимание, что управляемые ресурсы очищаются только в том случае, если значение disposing истинно, но неуправляемые ресурсы всегда очищаются.

public class MyResource: IDisposable
{
    // Pointer to an external unmanaged resource.
    private IntPtr handle;
    // Other managed resource this class uses.
    private Component component = new Component();
    // Track whether Dispose has been called.
    private bool disposed = false;

    // The class constructor.
    public MyResource(IntPtr handle)
    {
        this.handle = handle;
    }

    // Implement IDisposable.
    // Do not make this method virtual.
    // A derived class should not be able to override this method.
    public void Dispose()
    {
        Dispose(true);
        // This object will be cleaned up by the Dispose method.
        // Therefore, you should call GC.SupressFinalize to
        // take this object off the finalization queue
        // and prevent finalization code for this object
        // from executing a second time.
        GC.SuppressFinalize(this);
    }

    // Dispose(bool disposing) executes in two distinct scenarios.
    // If disposing equals true, the method has been called directly
    // or indirectly by a user's code. Managed and unmanaged resources
    // can be disposed.
    // If disposing equals false, the method has been called by the
    // runtime from inside the finalizer and you should not reference
    // other objects. Only unmanaged resources can be disposed.
    private void Dispose(bool disposing)
    {
        // Check to see if Dispose has already been called.
        if(!this.disposed)
        {
            // If disposing equals true, dispose all managed
            // and unmanaged resources.
            if(disposing)
            {
                // Dispose managed resources.
                component.Dispose();
            }

            // Call the appropriate methods to clean up
            // unmanaged resources here.
            // If disposing is false,
            // only the following code is executed.
            CloseHandle(handle);
            handle = IntPtr.Zero;

            // Note disposing has been done.
            disposed = true;

        }
    }

    // Use interop to call the method necessary
    // to clean up the unmanaged resource.
    [System.Runtime.InteropServices.DllImport("Kernel32")]
    private extern static Boolean CloseHandle(IntPtr handle);

    // Use C# destructor syntax for finalization code.
    // This destructor will run only if the Dispose method
    // does not get called.
    // It gives your base class the opportunity to finalize.
    // Do not provide destructors in types derived from this class.
    ~MyResource()
    {
        // Do not re-create Dispose clean-up code here.
        // Calling Dispose(false) is optimal in terms of
        // readability and maintainability.
        Dispose(false);
    }
}
18 голосов
/ 10 марта 2009

"Идея в том, что Dispose (Boolean) знает, является ли это вызывается сделать явную очистку (логическое верно) по сравнению с вызывается из-за сборки мусора (логическое значение ложно). это Различие полезно, потому что, когда будучи расположенным явно, Dispose (Boolean) метод может безопасно выполнить код, используя ссылочный тип поля, которые ссылаются на другие объекты зная наверняка, что эти другие объекты не были завершены или утилизировать еще. Когда логическое значение false, метод Dispose (Boolean) не должен выполнять код, который ссылается на поля ссылочного типа, потому что те объекты, возможно, уже были завершены ".

Гораздо больше информации содержится в «Руководстве по проектированию утилизации, завершения и управления ресурсами» .

Редактировать: ссылка.

9 голосов
/ 10 марта 2009

В C # нет деструкторов. Это финализатор, а это совсем другое.

Различие в том, нужно ли вам очищать управляемые объекты или нет. Вы не хотите пытаться очистить их в финализаторе, так как они сами могут быть завершены.


Недавно я случайно заглянул на страницу Деструкторы Руководства по программированию в C #. Это показывает, что я ошибся в своем ответе выше. В частности, существует разница между деструктором и финализатором:

class Car
{
    ~Car()  // destructor
    {
        // cleanup statements...
    }
}

эквивалентно

protected override void Finalize()
{
    try
    {
        // Cleanup statements...
    }
    finally
    {
        base.Finalize();
    }
}
3 голосов
/ 10 марта 2009

Я думаю, что путаница связана с тем, что в вашем примере вы не высвобождаете неуправляемые ресурсы. Они также должны быть освобождены, когда dispose вызывается через сборщик мусора, и они будут освобождены за пределами проверки для disposing. См. Пример MSDN, касающийся освобождения неуправляемых ресурсов . Другое, что могло бы произойти вне проверки, это вызов любого метода Dispose базового класса.

Из цитируемой статьи:

   protected override void Dispose(bool disposing) 
   {
      if (disposing) 
      {
         // Release managed resources.
      }
      // Release unmanaged resources.
      // Set large fields to null.
      // Call Dispose on your base class.
      base.Dispose(disposing);
   }
1 голос
/ 21 мая 2018

В следующем примере показано, как создать класс ресурсов, реализующий интерфейс IDisposable: https://msdn.microsoft.com/en-us/library/System.IDisposable.aspx

Функция In Dispose (bool dispose): если утилизация равна true, метод вызывается прямо или косвенно вашим кодом. Управляемые и неуправляемые ресурсы могут быть утилизированы. Если удаление равно false, метод был вызван средой выполнения из финализатора, и вы не должны ссылаться на другие объекты. Только неуправляемые ресурсы могут быть утилизированы.

1 голос
/ 10 марта 2009

Внутри if (распоряжения) вы должны вызывать dispose / close для управляемых объектов, которые имеют неуправляемые ресурсы (например, соединения с базой данных). Когда вызывается финализатор, эти объекты более недоступны, поэтому сами объекты могут быть завершены, и вы не нужно называть распоряжаться ими. Также порядок завершения не определен, поэтому вы можете вызывать dispose для уже удаленных объектов.

...