Compartir a través de


Capítulo 11: Modelos de diseño comunes, Implementar Finalize y Dispose para limpiar recursos no administrados

Publicado: 26 de junio de 2006

Patterns & Practices
Microsoft Corporation
Diciembre de 2002

imagen

https://msdn.microsoft.com/practices/

A menudo, las instancias de clases encapsulan el control sobre los recursos que no administra el motor de tiempo de ejecución, como identificadores de ventanas (HWND), conexiones a bases de datos, etc. Por consiguiente, deberá proporcionar las formas explícita e implícita de liberar esos recursos. Proporcione el control implícito implementando el Método Finalize protegido en un objeto (sintaxis de destructor de C# y de Extensiones administradas de C++). El recolector de elementos no utilizados llama a estos métodos en algún momento, cuando ya no hay ninguna referencia válida al objeto.

En esta página

Capítulo 11: Modelos de diseño comunes, Implementar Finalize y Dispose para limpiar recursos no administrados Capítulo 11: Modelos de diseño comunes, Implementar Finalize y Dispose para limpiar recursos no administrados

Capítulo 11: Modelos de diseño comunes, Implementar Finalize y Dispose para limpiar recursos no administrados

En algunos casos, se puede proporcionar a los programadores la utilización de un objeto con capacidad para liberar explícitamente estos recursos externos, antes de que el recolector de elementos no utilizados libere el objeto. Si el recurso externo es insuficiente o resulta muy caro, se puede conseguir un mejor rendimiento si el programador libera explícitamente los recursos cuando ya no se utilizan. Para proporcionar el control explícito, se implementa el método Dispose que proporciona la interfaz IDisposable. El consumidor del objeto deberá llamar a este método una vez hecho esto utilizando el objeto. Se puede llamar al método Dispose aunque todavía existan referencias al objeto.

Hay que tener en cuenta que cuando se proporciona el control explícito mediante el método Dispose, se debe proporcionar la limpieza implícita utilizando el método Finalize. Finalize proporciona una copia de seguridad para evitar que los recursos se pierdan permanentemente si el programador no llama al método Dispose.

Para obtener más información sobre la implementación de los métodos Finalize y Dispose para limpiar los recursos no administrados, vea Programar para la recolección de elementos no utilizados. En el siguiente ejemplo de código se muestra el modelo de diseño básico de implementación del método Dispose.

[Visual Basic]

' Design pattern for a base class.
Public Class Base
   Implements IDisposable
   ' Implement IDisposable.
   Public Overloads Sub Dispose() Implements IDisposable.Dispose
      Dispose(True)
      GC.SuppressFinalize(Me)
   End Sub

   Protected Overloads Overridable Sub Dispose(disposing As Boolean)
      If disposing Then
         ' Free other state (managed objects).
      End If
      ' Free your own state (unmanaged objects).
      ' Set large fields to null.
   End Sub

   Protected Overrides Sub Finalize()
      ' Simply call Dispose(False).
      Dispose (False)
   End Sub
End Class

' Design pattern for a derived class.
Public Class Derived
   Inherits Base

   Protected Overloads Overrides Sub Dispose(disposing As Boolean) 
      If disposing Then 
         ' Release managed resources.
      End If
      ' Release unmanaged resources.
      ' Set large fields to null.
      ' Call Dispose on your base class.
      Mybase.Dispose(disposing)
   End Sub
   ' The derived class does not have a Finalize method
   ' or a Dispose method with parameters because it inherits
   ' them from the base class.
End Class

[C#]

// Design pattern for a base class.
public class Base: IDisposable
{
   //Implement IDisposable.
   public void Dispose() 
   {
     Dispose(true);
      GC.SuppressFinalize(this); 
   }

   protected virtual void Dispose(bool disposing) 
   {
      if (disposing) 
      {
         // Free other state (managed objects).
      }
      // Free your own state (unmanaged objects).
      // Set large fields to null.
   }

   // Use C# destructor syntax for finalization code.
   ~Base()
   {
      // Simply call Dispose(false).
      Dispose (false);
   }
   
// Design pattern for a derived class.
public class Derived: Base
{   
   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);
   }
   // The derived class does not have a Finalize method
   // or a Dispose method with parameters because it inherits
   // them from the base class.
}

Para obtener un ejemplo de código más detallado del modelo de diseño de implementación de los métodos Finalize y Dispose, vea Implementar un método Dispose.

Personalizar el nombre del método Dispose

En algunos casos, es más adecuado utilizar un nombre específico del dominio que utilizar Dispose. Por ejemplo, en la encapsulación de un archivo se puede utilizar Close como el nombre del método. En este caso, se implementa Dispose de forma privada y se crea un método Close público que llama a Dispose. En el siguiente ejemplo de código se muestra este modelo. Se puede reemplazar Close con un nombre de método adecuado al dominio.

[Visual Basic]

' Do not make this method overridable.
' A derived class should not be allowed
' to override this method.
Public Sub Close()
   ' Call the Dispose method with no parameters.
   Dispose()
End Sub

[C#]

// Do not make this method virtual.
// A derived class should not be allowed
// to override this method.
public void Close()
{
   // Call the Dispose method with no parameters.
   Dispose();
}

Finalize

En las reglas siguientes se describen las instrucciones de uso del método Finalize:

  • Implemente Finalize sólo en objetos que requieran finalización. Hay algunos costos de rendimiento asociados con los métodos Finalize.

  • Si requiere un método Finalize, considere la posibilidad de implementar IDisposable para que los usuarios de la clase puedan evitar el costo de invocar el método Finalize.

  • No haga más visible el método Finalize. Este método debe ser de tipo protected y no de tipo public.

  • El método Finalize de un objeto debe liberar todos los recursos externos que posea el objeto. Además, un método Finalize debe liberar sólo los recursos que contiene el objeto. El método Finalize no debe hacer referencia a ningún otro objeto.

  • No llame directamente al método Finalize de un objeto que no sea de la clase base del objeto. Ésta no es una operación válida en el lenguaje de programación C#.

  • Llame al método base.Finalize desde un método Finalize del objeto.

Nota

Al método Finalize de la clase base se llama automáticamente con la sintaxis de destructor de C# y de las Extensiones administradas de C++.

Desechar

En las reglas siguientes se describen las instrucciones de uso del método Dispose:

  • Implemente el modelo de diseño Dispose en un tipo que encapsula recursos que deben ser liberados explícitamente. Los usuarios pueden liberar recursos externos llamando al método Dispose público.

  • Implemente el modelo de diseño Dispose en un tipo base que habitualmente tiene tipos derivados que contienen recursos, aunque la clase base no contenga ninguno. Si el tipo base contiene un método Close, esto suele indicar que se debe implementar el método Dispose. En este caso, no implemente un método Finalize en el tipo base. Finalize se debe implementar en todos los tipos derivados con recursos que es necesario limpiar.

  • Libere todos los recursos desechables que contenga un tipo en su método Dispose.

  • Una vez llamado el método Dispose en una instancia, evite ejecutar el método Finalize mediante una llamada al Método GC.SuppressFinalize. La excepción a esta regla son las situaciones muy poco habituales en las que el trabajo que no realiza el método Dispose se debe hacer con el método Finalize.

  • Llame al método Dispose de la clase base si implementa la interfaz IDisposable.

  • No dé por supuesto que se llamará al método Dispose. Los recursos no administrados que contiene un tipo también se deben liberar en un método Finalize, en el caso de que no se llame al método Dispose.

  • Inicie una excepción ObjectDisposedException a partir de los métodos de instancias de este tipo (distintos de Dispose) una vez que se haya deshecho de los recursos. Esta regla no se aplica al método Dispose porque se debería poder llamarlo varias veces sin iniciar una excepción.

  • Propague las llamadas al método Dispose a través de la jerarquía de tipos base. El método Dispose debe liberar todos los recursos que contenga un objeto y todos los objetos que contenga ese objeto. Por ejemplo, puede crear un objeto como TextReader que contenga los objetos Stream y Encoding, los dos creados por TextReader sin el conocimiento del usuario. Además, Stream y Encoding pueden adquirir recursos externos. Cuando llame al método Dispose en TextReader, éste deberá llamar a su vez al método Dispose en los objetos Stream y Encoding, que liberarán los recursos externos.

  • Considere la posibilidad de prohibir la utilización de un objeto después de llamar al método Dispose. La acción de volver a crear un objeto una vez desechado es un modelo difícil de implementar.

  • Permita que se llame al método Dispose más de una vez sin iniciar excepciones. El método no debe realizar ninguna acción después de la primera llamada.

Vea también

Instrucciones de diseño para programadores de bibliotecas de clases