2009-03-10 2 views
50

Ниже приводится типичный пример отчуждать картину:Зачем вызывать 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); 
    } 

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

ответ

38

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

Но обратите внимание, что если у вас нет неуправляемых ресурсов, тогда 'Dispose (false)' не имеет ничего общего, поэтому вам не нужен финализатор или 'Dispose (bool)' вообще. Я чувствую, что стандартная модель слишком сложна, чтобы обслуживать случаи использования, которые почти никогда не бывают (и когда они делают это, вероятно, плохая идея). Вот что я предпочитаю: http://nitoprograms.blogspot.com/2009/08/how-to-implement-idisposable-and.html –

+0

@romkyns «Основное использование [IDisposable] - освобождение неуправляемых ресурсов». (http://msdn.microsoft.com/en-us/library/System.IDisposable.aspx). Поэтому неудивительно, что стандартный способ реализации IDisposable - это больше, чем вам нужно, если у вас нет неуправляемых ресурсов. Я не уверен, что вы подразумеваете под «случаями использования, которые почти никогда не встречаются» - наличие смешанных управляемых и неуправляемых ресурсов не является неясным вариантом использования. –

+0

Тем не менее, я согласен, что если финализатор ничего не делает (потому что у вас нет неуправляемых ресурсов), вам это не нужно. Как и большинство моделей (как и большинство других, я думаю), имеет смысл использовать его там, где это имеет смысл. ;) –

8

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

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


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

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

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

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

так почему бы не просто опустить вызов вместе? – ryeguy

+0

Поскольку у вас еще есть неуправляемые ресурсы для очистки ... – Chris

+0

Но утилизация выполняется внутри цикла If, который не будет выполняться, когда переданный параметр является False (как из финализатора) – ryeguy

16

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

Там намного больше информации в “Dispose, Finalization, and Resource Management Design Guidelines” .

Edit:. ссылка

+1

Я отвечаю так же, как и на другой парень: зачем вообще звонить ему из финализатора? – ryeguy

+0

@ryeguy, потому что на самом деле вы не должны реализовывать финализатор самостоятельно, если вам действительно не нужно. –

+0

Ваша "гораздо больше информации" ссылка отличная! –

1

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

3

Я думаю, что путаница объясняется тем, что в вашем примере вы не выпускаете неуправляемые ресурсы. Они также должны быть выпущены, когда dispose вызывается через сбор мусора, и они будут выпущены за пределами чек на disposing. См. Пример MSDN, относящийся к releasing unmanaged resources. Другой, который должен/должен произойти вне проверки, - это вызов любого метода 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); 
    } 
+0

Что относительно '~ Derived() { Dispose (false); } 'для производных? –

Смежные вопросы