Зачем вызывать dispose (false) в деструкторе?


ниже приведен типичный пример шаблона dispose:

 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) в деструкторе? Если вы посмотрите на определение, оно абсолютно ничего не сделает, так зачем кому-то писать такой код? Разве это не имеет смысла просто не вызов dispose из деструктора вообще?

6 58

6 ответов:

финализатор используется как запасной вариант, если объект по какой-то причине не расположен должным образом. Обычно 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);
    }
}

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

там гораздо больше информации в "утилизация, доработка и руководство по проектированию управления ресурсами".

изменить: ссылка.

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

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


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

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

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

protected override void Finalize()
{
    try
    {
        // Cleanup statements...
    }
    finally
    {
        base.Finalize();
    }
}

Я думаю, что путаница связана с тем, что в вашем примере вы не выпускаете никаких неуправляемых ресурсов. Они также должны быть освобождены, когда 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);
   }

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

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

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