Respuestas:
Otros ya han cubierto la diferencia entre Dispose
y Finalize
(por cierto, el Finalize
método todavía se llama destructor en la especificación del lenguaje), por lo que solo agregaré un poco sobre los escenarios en los que el Finalize
método es útil.
Algunos tipos encapsulan los recursos desechables de una manera fácil de usar y los eliminan en una sola acción. El uso general es a menudo así: abrir, leer o escribir, cerrar (desechar). Encaja muy bien con la using
construcción.
Otros son un poco más difíciles. WaitEventHandles
para instancias no se usan así, ya que se usan para señalar de un hilo a otro. La pregunta entonces se convierte en quién debería recurrir Dispose
a estos. Como tipos de protección como estos, se implementa un Finalize
método que garantiza que los recursos se eliminen cuando la aplicación ya no haga referencia a la instancia.
Finalize
puede justificarse es cuando hay una serie de objetos que están interesados en mantener vivo un recurso, pero no hay ningún medio por el cual un objeto que deja de estar interesado en el recurso pueda averiguar si es el el último. En tal caso, Finalize
generalmente solo se disparará cuando nadie esté interesado en el objeto. El tiempo flojo de Finalize
es horrible para recursos no fungibles como archivos y bloqueos, pero puede estar bien para recursos fungibles.
Se llama al método finalizador cuando su objeto es basura recolectada y no tiene garantía de cuándo ocurrirá esto (puede forzarlo, pero perjudicará el rendimiento).
El Dispose
método, por otro lado, 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, identificadores de archivos, etc.) en el momento en que se realiza el código tu objeto
La práctica estándar es implementar IDisposable
y Dispose
para que pueda usar su objeto en una using
declaración. Tales como using(var foo = new MyObject()) { }
. Y en su finalizador, llama Dispose
, en caso de que el código de llamada olvidó deshacerse de usted.
Finalizar es el método de detención, llamado por el recolector de basura cuando reclama un objeto. Desechar es el método de "limpieza determinista", llamado por las aplicaciones para liberar valiosos recursos nativos (identificadores de ventana, conexiones de bases de datos, etc.) cuando ya no se necesitan, en lugar de dejarlos retenidos indefinidamente hasta que el GC llegue al objeto.
Como usuario de un objeto, siempre usa Dispose. Finalizar es para el GC.
Como implementador de una clase, si mantiene los recursos administrados que deben eliminarse, implementa Dispose. Si tiene recursos nativos, implementa Dispose y Finalize, y ambos llaman a un método común que libera los recursos nativos. Estos modismos suelen combinarse mediante un método privado Dispose (bool disposing), que Dispose llama con verdadero y Finaliza llamadas con falso. Este método siempre libera recursos nativos, luego verifica el parámetro de eliminación y, si es cierto, elimina los recursos administrados y llama a GC.SuppressFinalize.
Dispose
es bueno, e implementarlo correctamente es generalmente fácil. Finalize
es malo, e implementarlo correctamente es generalmente difícil. Entre otras cosas, debido a que el GC se asegurará de que la identidad de ningún objeto sea "reciclada" mientras exista alguna referencia a ese objeto, es fácil limpiar un montón de Disposable
objetos, algunos de los cuales pueden haber sido limpiados. No hay problema; Cualquier referencia a un objeto sobre el que Dispose
ya se ha llamado seguirá siendo una referencia a un objeto sobre el que Dispose
ya se ha llamado.
Fred
posee el identificador de archivo # 42 y lo cierra, el sistema puede adjuntar ese mismo número a algún identificador de archivo que se le da a otra entidad. En ese caso, el identificador de archivo # 42 no se referiría al archivo cerrado de Fred, sino al archivo que estaba en uso activo por esa otra entidad; para Fred
tratar de cerrar la manija # 42 de nuevo sería desastroso. Intentar hacer un seguimiento 100% confiable de si un objeto no administrado ha sido liberado aún es viable. Intentar hacer un seguimiento de varios objetos es mucho más difícil.
Finalizar
protected
, no public
o private
para que el método no se pueda invocar directamente desde el código de la aplicación y, al mismo tiempo, se puede llamar al base.Finalize
métodoDisponer
IDisposable
en cada tipo que tenga un finalizadorDispose
método. En otras palabras, evite usar un objeto después de que el Dispose
método haya sido invocado.Dispose
a todos los IDisposable
tipos una vez que haya terminado con ellosDispose
ser llamado varias veces sin generar errores.Dispose
método utilizando el GC.SuppressFinalize
métodoDispose
métodosEliminar / Patrón finalizado
Dispose
y Finalize
cuando trabaje con recursos no administrados. La Finalize
implementación se ejecutaría y los recursos aún se liberarían cuando el objeto se recolecte basura, incluso si un desarrollador no llamara al Dispose
método explícitamente.Finalize
método, así como en el Dispose
método. Además, llame al Dispose
método para cualquier objeto .NET que tenga como componentes dentro de esa clase (que tenga recursos no administrados como miembro) del Dispose
método.Finalizar recibe una llamada del GC cuando este objeto ya no está en uso.
Dispose es solo un método normal al 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 del libro MCSD Certification Toolkit (examen 70-483) pág. 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 gestionados ni recursos no gestionados , no debería implementar IDisposable
ni tener un destructor.
Si la clase solo ha administrado recursos , debería implementarse IDisposable
pero no debería tener un destructor. (Cuando se ejecuta el destructor, no puede estar seguro de que todavía existan objetos administrados, por lo que no puede llamar a sus Dispose()
métodos de todos modos).
Si la clase solo tiene recursos no administrados , debe implementarse IDisposable
y necesita un destructor en caso de que el programa no llame Dispose()
.
Dispose()
El método debe ser seguro para ejecutarse más de una vez. Puede lograrlo utilizando una variable para realizar un seguimiento de si se ha ejecutado anteriormente.
Dispose()
debería liberar recursos gestionados y no gestionados .
El destructor solo debe liberar recursos no administrados . Cuando se ejecuta el destructor, no puede estar seguro de que todavía existan objetos administrados, por lo que no puede llamar a sus métodos Dispose de todos modos. Esto se obtiene utilizando el protected void Dispose(bool disposing)
patrón canónico , donde solo los recursos administrados se liberan (se eliminan) cuando disposing == true
.
Después de liberar recursos, Dispose()
debe llamarGC.SuppressFinalize
para que el objeto pueda omitir la cola de finalización.
Un ejemplo de una implementación para una clase con recursos no administrados y 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);
// We don't need the destructor because
// our resources are already freed.
GC.SuppressFinalize(this);
}
// 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;
}
}
}
El 99% de las veces, tampoco debería preocuparse. :) Pero, si sus objetos contienen referencias a recursos no administrados (identificadores de ventana, identificadores de archivos, por ejemplo), debe proporcionar una forma para que su objeto administrado libere esos recursos. Finalizar 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 una liberación de recursos y se puede llamar directamente.
Hay mucho más que aprender sobre el tema de la recolección de basura , pero eso es un comienzo.
El finalizador es para la limpieza implícita: debe usar esto siempre que una clase administre recursos que absolutamente deben limpiarse, ya que de lo contrario se perderían identificadores / memoria, etc.
Implementar correctamente un finalizador es notoriamente difícil y debe evitarse siempre que sea posible: la SafeHandle
clase (disponible en .Net v2.0 y superior) ahora significa que muy raramente (si alguna vez) necesita implementar un finalizador más.
La IDisposable
interfaz es para la limpieza explícita y se usa mucho más comúnmente; debe usarla para permitir a los usuarios liberar o limpiar recursos explícitamente cada vez que hayan terminado de usar un objeto.
Tenga en cuenta que si tiene un finalizador, también debe implementar la IDisposable
interfaz para permitir que los usuarios liberen explícitamente esos recursos antes de lo que serían si el objeto fuera basura recolectada.
Consulte la Actualización de DG: Desechar, Finalizar y Gestión de Recursos para lo que considero el mejor y más completo conjunto de recomendaciones sobre finalizadores y IDisposable
.
El resumen es -
Además, otra diferencia es que, en la implementación Dispose (), también debe liberar los recursos administrados , mientras que eso no debe hacerse en el Finalizador. Esto se debe a que es muy probable que los recursos administrados a los que hace referencia el objeto ya se hayan limpiado antes de que esté listo para finalizar.
Para una clase que usa recursos no administrados, la mejor práctica es definir ambos, el método Dispose () y el Finalizador, para usarlos como respaldo en caso de que un desarrollador olvide deshacerse explícitamente del 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 i.e. 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);
}
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 finalize para reclamar los recursos no administrados (como el funcionamiento del archivo, la API de Windows, la conexión de red, la conexión de la base de datos), pero el tiempo no está fijo cuando GC lo llamaría. GC lo llama implícitamente, significa que no tenemos un control de bajo nivel sobre él.
Método de eliminación: tenemos un control de bajo nivel sobre él como lo llamamos desde el código. podemos reclamar los recursos no administrados siempre que consideremos que no son utilizables. Podemos lograr esto implementando el patrón de eliminación.
Las instancias de clase a menudo encapsulan el control sobre los recursos que no son administrados por el tiempo de ejecución, como los identificadores de ventana (HWND), las conexiones de la base de datos, etc. Por lo tanto, debe proporcionar una forma explícita e implícita de liberar esos recursos. Proporcione control implícito mediante la implementación del Método de finalización protegido en un objeto (sintaxis de destructor en C # y 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 para el objeto. En algunos casos, es posible que desee proporcionar a los programadores que usan 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 termine de usar el objeto. Se puede llamar a Dispose incluso si otras referencias al objeto están vivas.
Tenga en cuenta que incluso cuando proporciona un control explícito a través de Dispose, debe proporcionar una limpieza implícita utilizando el método Finalize. Finalizar proporciona una copia de seguridad para evitar que los recursos se filtren permanentemente si el programador no puede llamar a Eliminar.
La principal diferencia entre Dispose y Finalize es que:
Dispose
generalmente se llama por su código. Los recursos se liberan instantáneamente cuando lo llamas. La gente se olvida de llamar al método, por lo using() {}
que se inventa una declaración. Cuando su programa termine la ejecución del código dentro del {}
, llamará al Dispose
método automáticamente.
Finalize
no es llamado por su código. Está destinado a ser llamado por el recolector de basura (GC). Eso significa que el recurso podría liberarse en cualquier momento en el futuro cuando GC decida hacerlo. Cuando GC haga su trabajo, pasará por muchos métodos de Finalización. Si tiene una lógica pesada en esto, hará que el proceso sea lento. Puede causar problemas de rendimiento para su programa. Así que ten cuidado con lo que pones allí.
Yo personalmente escribiría la mayor parte de la lógica de destrucción en Dispose. Con suerte, esto aclara la confusión.
Como sabemos, disponer y finalizar ambos se utilizan para liberar recursos no administrados ... pero la diferencia es que finalizar utiliza dos ciclos para liberar los recursos, mientras que la disposición utiliza un ciclo.
Para responder en la primera parte, debe proporcionar ejemplos en los que las personas usan un enfoque diferente para el mismo objeto de clase. De lo contrario, es difícil (o incluso extraño) responder.
En cuanto a la segunda pregunta, mejor lea primero este uso adecuado de la interfaz IDisposable que afirma que
¡Es tu elección! Pero elija Eliminar.
En otras palabras: el GC solo conoce el finalizador (si existe. También conocido como destructor para Microsoft). Un buen código intentará limpiar desde ambos (finalizador y Eliminar).