Finalizar vs Dispose

¿Por qué algunas personas usan el método Finalize sobre el método Dispose ?

¿En qué situaciones Finalize método Finalize sobre el método Dispose y viceversa?

Otros ya han cubierto la diferencia entre Dispose y Finalize (por cierto, el método Finalize todavía se llama destructor en la especificación del lenguaje), así que solo añadiré un poco sobre los escenarios donde el método Finalize es útil.

Algunos tipos encapsulan los recursos desechables de una manera que es fácil de usar y eliminarlos en una sola acción. El uso general suele ser así: abrir, leer o escribir, cerrar (Eliminar). Encaja muy bien con la construcción que using .

Otros son un poco más difíciles. WaitEventHandles para instancias no se usan así porque se utilizan para señalar de un hilo a otro. La pregunta entonces es ¿quién debería llamar a Dispose sobre esto? Como tipos de salvaguarda como estos implementan un método Finalize , que asegura que los recursos se eliminen cuando la aplicación ya no haga referencia a la instancia.

El método del finalizador se invoca cuando su objeto es basura y no tiene garantía de cuándo ocurrirá (puede forzarlo, pero dañará el rendimiento).

Por otro lado, el método Dispose debe ser llamado por el código que creó su clase para que pueda limpiar y liberar los recursos que haya adquirido (datos no administrados, conexiones de bases de datos, manejadores de archivos, etc.) en el momento en que se realiza el código con tu objeto

La práctica estándar es implementar IDisposable y Dispose para que pueda usar su objeto en una statement de using . Como using(var foo = new MyObject()) { } . Y en su finalizador, llama a Dispose , en caso de que el código de llamada se haya olvidado de ti.

Finalize es el método de protección, llamado por el recolector de basura cuando recupera un objeto. Dispose es el método de “limpieza determinista”, llamado por las aplicaciones para liberar recursos nativos valiosos (identificadores de ventanas, conexiones de bases de datos, etc.) cuando ya no son necesarios, en lugar de dejarlos retenidos indefinidamente hasta que el GC se aproxime al objeto.

Como usuario de un objeto, siempre utilizas Dispose. Finalize es para el GC.

Como implementador de una clase, si tiene recursos administrados que deberían eliminarse, implementará Dispose. Si tiene recursos nativos, implementa Dispose y Finalize, y ambos invocan un método común que libera los recursos nativos. Estas expresiones idiomáticas se combinan típicamente a través de un método de Disposición (bool disposing) privado, que Descarta llamadas con verdadero, y Finaliza las llamadas con falso. Este método siempre libera recursos nativos, luego verifica el parámetro de eliminación y, si es cierto, dispone recursos administrados y llama a GC.SuppressFinalize.

Finalizar

  • Los finalizadores siempre deben estar protected , no son public ni private modo que el método no puede base.Finalize directamente desde el código de la aplicación y, al mismo tiempo, puede realizar una llamada a la base.Finalize Método de base.Finalize
  • Los finalizadores deben liberar recursos no administrados solamente.
  • El marco no garantiza que un finalizador se ejecute en absoluto en una instancia determinada.
  • Nunca asigne memoria en los finalizadores ni llame a los métodos virtuales desde los finalizadores.
  • Evite la sincronización y el aumento de excepciones no controladas en los finalizadores.
  • El orden de ejecución de los finalizadores no es determinista; en otras palabras, no puede confiar en que otro objeto siga estando disponible en su finalizador.
  • No defina finalizadores en tipos de valores.
  • No crees destructores vacíos. En otras palabras, nunca debe definir explícitamente un destructor a menos que su clase necesite limpiar recursos no administrados y si define uno, debería hacer algo de trabajo. Si, más adelante, ya no necesita limpiar recursos no administrados en el destructor, elimínelos por completo.

Disponer

  • Implementar IDisposable en cada tipo que tenga un finalizador
  • Asegúrese de que un objeto quede inutilizable después de realizar una llamada al método Dispose . En otras palabras, evite usar un objeto después de que se haya invocado el método Dispose .
  • Llame a Dispose en todos los tipos de IDisposable una vez que haya terminado con ellos
  • Permita que se llame a Dispose varias veces sin generar errores.
  • Suprima las llamadas posteriores al finalizador desde el método Dispose utilizando el método GC.SuppressFinalize
  • Evite crear tipos de valores desechables
  • Evite lanzar excepciones desde el interior de los métodos de Dispose

Dispose / Finalized Pattern

  • Microsoft recomienda que implemente Dispose y Finalize cuando trabaje con recursos no administrados. La implementación de Finalize se ejecutaría y los recursos seguirían siendo liberados cuando el objeto fuera recolectado como basura, incluso si un desarrollador no llamara explícitamente al método Dispose .
  • Limpiar los recursos no administrados en el método Finalize así como en el método Dispose . Además, llame al método Dispose para cualquier objeto .NET que tenga como componentes dentro de esa clase (que tenga recursos no administrados como miembro) desde el método Dispose .

Finalize es llamado por el GC cuando este objeto ya no está en uso.

Dispose es solo un método normal que el usuario de esta clase puede llamar para liberar cualquier recurso.

Si el usuario olvidó llamar a Dispose y si la clase tiene Finalize implementado, GC se asegurará de que se llame.

Hay algunas claves acerca del libro MCSD Certification Toolkit (examen 70-483) pag 193:

destructor ≈ (es casi igual a) base.Finalize () , El destructor se convierte en una versión de anulación del método Finalize que ejecuta el código del destructor y luego llama al método Finalize de la clase base. Entonces es totalmente no determinista, no se puede saber cuándo se llamará porque depende de GC.

Si una clase no contiene recursos administrados ni recursos no administrados , no necesita implementar IDisposable o tener un destructor.

Si la clase solo tiene recursos administrados , debe implementar IDisposable pero no necesita un destructor. (Cuando se ejecuta el destructor, no puede estar seguro de que aún existan objetos gestionados, por lo que no puede llamar a sus métodos de eliminación de todos modos).

Si la clase solo tiene recursos no administrados , necesita implementar IDisposable y necesita un destructor en caso de que el progtwig no llame a Dispose.

El método de eliminación debe ser seguro para ejecutar más de una vez. Puede lograr eso utilizando una variable para realizar un seguimiento de si se ha ejecutado antes.

El método Dispose debería liberar recursos administrados y no administrados .

El destructor debería liberar solo recursos no administrados . (Cuando se ejecuta el destructor, no puede estar seguro de que aún existan objetos gestionados, por lo que no puede llamar a sus métodos de eliminación de todos modos).

Después de liberar recursos, el destructor debe llamar a GC.SuppressFinalize , por lo que el objeto puede omitir la cola de finalización.

Un ejemplo de una implementación para una clase con recursos administrados y no administrados:

 using System; class DisposableClass : IDisposable { // A name to keep track of the object. public string Name = ""; // Free managed and unmanaged resources. public void Dispose() { FreeResources(true); } // Destructor to clean up unmanaged resources // but not managed resources. ~DisposableClass() { FreeResources(false); } // Keep track if whether resources are already freed. private bool ResourcesAreFreed = false; // Free resources. private void FreeResources(bool freeManagedResources) { Console.WriteLine(Name + ": FreeResources"); if (!ResourcesAreFreed) { // Dispose of managed resources if appropriate. if (freeManagedResources) { // Dispose of managed resources here. Console.WriteLine(Name + ": Dispose of managed resources"); } // Dispose of unmanaged resources here. Console.WriteLine(Name + ": Dispose of unmanaged resources"); // Remember that we have disposed of resources. ResourcesAreFreed = true; // We don't need the destructor because // our resources are already freed. GC.SuppressFinalize(this); } } } 

99% de las veces, no deberías tener que preocuparte tampoco. 🙂 Pero, si sus objetos contienen referencias a recursos no administrados (controladores de ventanas, manejadores de archivos, por ejemplo), debe proporcionar una forma para que el objeto administrado libere esos recursos. Finalize da control implícito sobre la liberación de recursos. Es llamado por el recolector de basura. Dispose es una forma de dar un control explícito sobre un lanzamiento de recursos y se puede invocar directamente.

Hay mucho más por aprender sobre el tema de Garbage Collection , pero eso es un comienzo.

El finalizador es para la limpieza implícita: debe usar esto siempre que una clase administre recursos que deben limpiarse completamente, ya que de lo contrario se perdería el control / memoria, etc.

Implementar correctamente un finalizador es notoriamente difícil y debe evitarse siempre que sea posible: la clase SafeHandle (disponible en .Net v2.0 y superior) ahora significa que muy raramente (si es que alguna vez) necesita implementar un finalizador.

La interfaz IDisposable es para limpieza explícita y se usa mucho más comúnmente; debe usar esto para permitir a los usuarios liberar o limpiar recursos de forma explícita cada vez que hayan terminado de usar un objeto.

Tenga en cuenta que si tiene un finalizador, entonces también debe implementar la interfaz IDisposable para permitir a los usuarios liberar esos recursos de manera más IDisposable de lo que lo serían si el objeto fuera basura.

Consulte Actualización de DG: Deshacer, Finalización y Gestión de recursos para lo que considero que es el mejor y más completo conjunto de recomendaciones sobre finalizadores e IDisposable .

El mejor ejemplo que conozco

  public abstract class DisposableType: IDisposable { bool disposed = false; ~DisposableType() { if (!disposed) { disposed = true; Dispose(false); } } public void Dispose() { if (!disposed) { disposed = true; Dispose(true); GC.SuppressFinalize(this); } } public void Close() { Dispose(); } protected virtual void Dispose(bool disposing) { if (disposing) { // managed objects } // unmanaged objects and resources } } 

Diferencia entre los métodos Finalizar y Eliminar en C #.

GC llama al método de finalización para reclamar los recursos no administrados (como la operación de archivos, la API de Windows, la conexión de red, la conexión de la base de datos), pero el tiempo no es fijo cuando GC lo llama. Se llama implícitamente por GC significa que no tenemos bajo control de nivel sobre él.

Método de eliminación: tenemos bajo control de nivel como lo llamamos desde el código. podemos reclamar los recursos no administrados siempre que consideremos que no se pueden usar. Podemos lograr esto implementando el patrón IDisposal.

Las instancias de clase a menudo encapsulan el control sobre los recursos que no son administrados por el tiempo de ejecución, como manejadores de ventanas (HWND), conexiones de bases de datos, etc. Por lo tanto, debe proporcionar una forma explícita e implícita de liberar esos recursos. Proporcione un control implícito mediante la implementación del método Finalize protegido en un objeto (syntax del destructor en C # y las extensiones administradas para C ++). El recolector de basura llama a este método en algún momento después de que ya no hay referencias válidas al objeto. En algunos casos, es posible que desee proporcionar a los progtwigdores que utilizan un objeto la capacidad de liberar explícitamente estos recursos externos antes de que el recolector de basura libere el objeto. Si un recurso externo es escaso o costoso, se puede lograr un mejor rendimiento si el progtwigdor libera recursos explícitamente cuando ya no se utilizan. Para proporcionar un control explícito, implemente el método Dispose proporcionado por la interfaz IDisposable. El consumidor del objeto debe llamar a este método cuando se hace usando el objeto. Dispose se puede invocar incluso si otras referencias al objeto están activas.

Tenga en cuenta que incluso cuando proporciona un control explícito a través de Dispose, debe proporcionar una limpieza implícita con el método Finalize. Finalize proporciona una copia de seguridad para evitar que los recursos se filtren permanentemente si el progtwigdor no puede llamar a Dispose.

El resumen es –

  • Usted escribe un finalizador para su clase si hace referencia a recursos no administrados y desea asegurarse de que esos recursos no administrados se liberen cuando una instancia de esa clase se recolecta basura automáticamente . Tenga en cuenta que no puede llamar al Finalizer de un objeto de forma explícita: el recolector de elementos no utilizados lo llama automáticamente cuando lo considere necesario.
  • Por otro lado, implementa la interfaz IDisposable (y consecuentemente define el método Dispose () como resultado de su clase) cuando su clase hace referencia a recursos no administrados, pero no quiere esperar a que el recolector de basura lo active. (que puede ser en cualquier momento, sin control del progtwigdor) y desea liberar esos recursos tan pronto como haya terminado. Por lo tanto, puede liberar explícitamente recursos no administrados llamando al método Dispose () de un objeto.

Además, otra diferencia es que, en la implementación de Dispose (), también debería liberar los recursos administrados , mientras que eso no debería hacerse en el Finalizer. Esto se debe a que es muy probable que los recursos gestionados a los que hace referencia el objeto ya se hayan limpiado antes de que estén listos para su finalización.

Para una clase que utiliza recursos no administrados, la mejor práctica es definir ambos, el método Dispose () y el Finalizer, para utilizar como respaldo en caso de que un desarrollador se olvide de eliminar explícitamente el objeto. Ambos pueden usar un método compartido para limpiar recursos administrados y no administrados:

 class ClassWithDisposeAndFinalize : IDisposable { // Used to determine if Dispose() has already been called, so that the finalizer // knows if it needs to clean up unmanaged resources. private bool disposed = false; public void Dispose() { // Call our shared helper method. // Specifying "true" signifies that the object user triggered the cleanup. CleanUp(true); // Now suppress finalization to make sure that the Finalize method // doesn't attempt to clean up unmanaged resources. GC.SuppressFinalize(this); } private void CleanUp(bool disposing) { // Be sure we have not already been disposed! if (!this.disposed) { // If disposing equals true ie if disposed explicitly, dispose all // managed resources. if (disposing) { // Dispose managed resources. } // Clean up unmanaged resources here. } disposed = true; } // the below is called the destructor or Finalizer ~ClassWithDisposeAndFinalize() { // Call our shared helper method. // Specifying "false" signifies that the GC triggered the cleanup. CleanUp(false); } 

Como ya sabemos disponer y finalizar, ambos se utilizan para liberar recursos no gestionados … pero la diferencia es que finaliza utiliza dos ciclos para liberar los recursos, donde como disponer utiliza un ciclo …