Destructor vs IDisposable?

He leído sobre la eliminación de objetos / interfaz IDisposable y destructores en C #, pero a mí parece que hacen lo mismo.

¿Cuál es la diferencia entre los dos? ¿Por qué debería usar uno sobre el otro? De hecho, en este ejemplo (enlace a continuación) este código usa tanto la interfaz IDisposable como un destructor:

http://msdn.microsoft.com/en-us/library/system.idisposable.aspx

El comentario dice que el destructor es si el código de finalización no se usa, pero ¿cómo decido cuándo usar uno sobre el otro?

Escribí una publicación bastante detallada que debería ayudar a explicar sobre los finalizadores, IDisposable, y cuándo deberías usar una u otra: http://gregbee.ch/blog/implementing-and-using-the-dispositable-interface

Probablemente la parte más relevante se cita a continuación:

Cuando usa recursos no administrados, como identificadores y conexiones de bases de datos, debe asegurarse de que se mantengan durante el tiempo mínimo, utilizando el principio de adquirir tarde y liberar anticipadamente. En C ++, la liberación de los recursos normalmente se realiza en el destructor, que se ejecuta determinísticamente en el punto donde se elimina el objeto. El tiempo de ejecución de .NET, sin embargo, utiliza un recolector de elementos no utilizados (GC) para limpiar y recuperar la memoria utilizada por objetos que ya no son accesibles; como esto se ejecuta de forma periódica, significa que el punto en el que se limpia el objeto no es determinista. La consecuencia de esto es que los destructores no existen para los objetos administrados, ya que no existe un lugar determinista para ejecutarlos.

En lugar de destructores, C # tiene finalizadores que se implementan anulando el método Finalize definido en la clase de objeto base (aunque C # usa confusamente la syntax del destructor de C ++ ~ Object para esto). Si un objeto prevalece sobre el método Finalize, en lugar de ser recogido por el GC cuando está fuera del scope, el GC lo coloca en una cola de finalizador. En el siguiente ciclo de GC, se ejecutan todos los finalizadores en la cola (en un solo hilo en la implementación actual) y se recupera la memoria de los objetos finalizados. Es bastante obvio de esto por qué no desea hacer una limpieza en un finalizador: se necesitan dos ciclos de GC para recolectar el objeto en lugar de uno y hay un solo subproceso donde todos los finalizadores se ejecutan mientras que cada otro subproceso se suspende, por lo que va a doler el rendimiento.

Entonces, si no tiene destructores y no desea dejar la limpieza al finalizador, entonces la única opción es limpiar el objeto de forma manual y determinista. Ingrese la interfaz IDisposable que proporciona un estándar para admitir esta funcionalidad y define un único método, Dispose, donde pone la lógica de limpieza para el objeto. Cuando se utiliza dentro de un bloque finally, esta interfaz proporciona una funcionalidad equivalente a los destructores. El motivo de los bloques finalmente en el código es principalmente para admitir la interfaz IDisposable; esta es la razón por la que C ++ usa simplemente try / except ya que no hay necesidad de un bloque final con destructores.

Version corta

Un finalizador le brinda la oportunidad de deshacerse de los recursos no administrados en caso de que el usuario de su objeto se haya olvidado de llamar IDisposable.Dispose . IDisposable.Dispose .

Si su objeto implementa IDisposable , el usuario de su objeto debe llamar a .Dispose . No es necesario que limpie el desorden del usuario; pero es algo bueno de hacer


Mi respuesta más popular en Stackoverflow lo guía desde el principio por qué tiene IDisposable, qué debería hacer, qué puede hacer su finalizador, qué no debería hacer.

Esta respuesta derrite las caras

ha sido utilizado para describirlo: P

Tener destructor (~ Object ()) en el lenguaje de progtwigción administrado es la idea más tonta. Tiene sentido perfectamente que los lenguajes no administrados como C, C ++ tengan destructores ya que usan modismos RAII pero para manejados como Java, C #, tan absurdos.

Joshua Bloch, un antiguo líder de proyecto en Java Collection Framework, señaló que la idea del método finalize () (que es equivalente a C ++ como destructor de C ++) en Java fue el mayor error jamás cometido. Igual que C #, finallize () en Java da una sobrecarga a “nuevo”, ya que debe agregarse a la cola del finalizador durante la asignación. Además, Garbage Collector debe abrir y ejecutar finallize () en la cola, por lo que dos veces la sobrecarga durante gc.

C # tenía muchas características mejoradas como “using (IDisposable) {}” que no solo permite que la variable IDisposable se limite al scope del bloque “using” sino que también garantiza su limpieza. Mi pregunta es, ¿por qué C # siguió el mismo camino de Java que condujo a un gran error. Puede ser que si el desarrollo de dotnet comenzó después de 2003 ~ 2005, cuando muchos arquitectos de Java encontraron la falacia de finallize (), entonces el error se habría evitado.

Muchas buenas ideas de un idioma a menudo se transfieren a otro lenguaje como el “IDisposable / using combo” en C # que se transfirió a Java 1.7 en su statement “try (object-to-dispose) {}”. Pero es una lástima que los arquitectos del lenguaje no descubran la mala idea disfrazada de buena idea durante su transferencia de uno a otro.

Mi consejo es nunca utilizar ~ Destructor () y seguir con IDisposable / using combo si necesita limpiar manualmente el recurso no administrado como las conexiones de la base de datos.