2009-03-10 13 views
50

Lo que sigue es un ejemplo típico patrón de disponer:¿Por qué invocar dispose (false) en el destructor?

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); 
    } 

entiendo lo deseche hace, pero lo que no entiendo es por qué usted quiere llamar a disponer (falso) en el destructor? Si nos fijamos en la definición, no haría absolutamente nada, entonces ¿por qué alguien escribiría un código como este? ¿No tendría sentido simplemente llamar deshacerse del destructor?

Respuesta

38

El finalizador se utiliza como un recurso si el objeto no está dispuesto adecuadamente por alguna razón. Normalmente se llamaría al método Dispose(), que elimina la conexión del finalizador y convierte el objeto en un objeto administrado regular que el recolector de basura puede eliminar fácilmente.

Aquí hay un ejemplo de MSDN de una clase que tiene recursos administrados y no administrados para limpiar.

Observe que los recursos administrados solo se limpian si disposing es verdadero, pero los recursos no administrados siempre se limpian.

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

Pero tenga en cuenta que si no tiene recursos no administrados, 'Dispose (false)' no tiene exactamente nada que ver, por lo que no necesita un finalizador ni un 'Dispose (bool)' en absoluto. Siento que el patrón estándar es demasiado complicado para atender casos de uso que casi nunca ocurren (y cuando lo hacen probablemente sean una mala idea). Este es uno que prefiero: http://nitoprograms.blogspot.com/2009/08/how-to-implement-disposable-and.html –

+0

@romkyns "El uso principal de [IDisposable] es liberar recursos no administrados". (http://msdn.microsoft.com/en-us/library/System.IDisposable.aspx) Por lo tanto, no es sorprendente que la forma estándar de implementar IDisposable sea más de lo que necesita si no tiene recursos no administrados. No estoy seguro de lo que quiere decir con "casos de uso que casi nunca ocurren": tener una combinación de recursos administrados y no administrados no es un caso de uso oscuro. –

+0

Dicho esto, acepto que si el finalizador no está haciendo nada (porque no tiene recursos no administrados), entonces no lo necesita. Como la mayoría de los patrones (como casi cualquier cosa, supongo), tiene sentido usarlo donde tenga sentido. ;) –

8

No hay destructores en C#. Eso es un Finalizer, que es algo diferente.

La distinción es si necesita limpiar objetos administrados o no. No desea intentar limpiarlos en el finalizador, ya que ellos mismos pueden haber sido finalizados.


Sólo recientemente se pasó a mirar la página Destructors de la Guía de programación de C#. Muestra que estaba equivocado en mi respuesta, arriba. En particular, hay una diferencia entre el destructor y el finalizador:

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

es equivalente a

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

¿por qué no simplemente omitir la llamada todos juntos? – ryeguy

+0

Porque es posible que aún tenga recursos no administrados para limpiar ... – Chris

+0

Pero la eliminación se realiza dentro del bucle If, ​​que no se ejecutará cuando el parámetro pasado es False (desde el finalizador) – ryeguy

16

"La idea aquí es que Dispose (Boolean) sabe si se siendo llamado a hacer una limpieza explícita (el Booleano es verdadero) versus ser llamado debido a una recolección de basura (el Booleano es falso). Esta distinción es útil porque, cuando estando dispuestos de forma explícita, el método Dispose (Boolean) con seguridad puede ejecutar código utilizando el tipo de referencia campos que hacen referencia a otros objetos sabiendo con certeza que estos otros objetos no se han concluido o eliminados todavía. Cuando el booleano es falso, el Dispose (Boolean) Método no debería ejecutar código que se refieren a campos de tipo de referencia porque esos objetos pueden ya han sido finalizado."

Hay mucha más información en la “Dispose, Finalization, and Resource Management Design Guidelines” .

Editar:. enlace

+1

Respondo de la misma manera que hice a el otro tipo: ¿por qué llamarlo para nada del finalizador? – ryeguy

+0

@ryeguy porque en realidad no deberías implementar el finalizador a menos que realmente tengas que hacerlo. –

+0

Su enlace "mucho más información" es genial! –

1

Dentro del caso (desechar) que se supone que llamar a desechar/cierre en los objetos administrados que tienen recursos no administrados (por ejemplo, conexiones de base de datos) .Cuando el finalizador está llamado a estos ob Los objetos ya no se pueden alcanzar, por lo que los objetos mismos se pueden finalizar y no es necesario llamarlos para disponer de ellos. Además, el orden de finalización no está terminado, por lo que puede estar llamando a deshacerse de los objetos ya dispuestos.

3

Creo que la confusión se debe al hecho de que en su ejemplo no está liberando ningún recurso no administrado. También deben liberarse cuando se solicite el desecho a través de la recolección de basura y se liberarían fuera de el cheque para disposing. Consulte el ejemplo de MSDN relacionado con releasing unmanaged resources. El otro que eso/debería ocurrir fuera del cheque es una llamada a cualquier método de eliminación de la clase base.

Desde el artículo citado:

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

¿Qué pasa con '~ Derived() { Dispose (false); } 'para derivado? –

Cuestiones relacionadas