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

Далее следует типичный пример шаблона размещения:

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

Ответ 1

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

Ответ 2

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

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

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

Ответ 3

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

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


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

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

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

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

Ответ 4

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

Ответ 5

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