pattern - recursos no administrados c#
Finalizar vs Dispose (13)
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.
¿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?
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 ...
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.
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 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 declaración 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.
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
}
}
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.
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.
Ha pasado mucho tiempo, pero puede leer este http://blog.stephencleary.com/2009/08/how-to-implement-idisposable-and.html
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 programa 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);
}
}
}
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 (sintaxis 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 programadores 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 programador 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 programador no puede llamar a Dispose.
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.
Finalizar
- Los finalizadores siempre deben estar
protected
, no sonpublic
niprivate
modo que el método no puedebase.Finalize
directamente desde el código de la aplicación y, al mismo tiempo, puede realizar una llamada a labase.Finalize
Método debase.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étodoDispose
. - Llame a
Dispose
en todos los tipos deIDisposable
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étodoGC.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
yFinalize
cuando trabaje con recursos no administrados. La implementación deFinalize
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étodoDispose
. - Limpiar los recursos no administrados en el método
Finalize
así como en el métodoDispose
. Además, llame al métodoDispose
para cualquier objeto .NET que tenga como componentes dentro de esa clase (que tenga recursos no administrados como miembro) desde el métodoDispose
.