2009-03-10 15 views
9

Entiendo que se utiliza para desasignar recursos no administrados, sin embargo, no estoy seguro de cuándo se llama realmente Dispose. Sé que se llama al final de un bloque using, pero ¿también se invoca cuando se recoge el objeto?¿Cómo funciona la interfaz IDisposable?

+0

Lo siento, Mark, pero estás ladrando en el árbol equivocado, posiblemente en el bosque equivocado. Mira las referencias en las otras respuestas si no me crees. –

+1

Guau, ¿por qué alguien vota para cerrar? ¡Este es un gran debate! –

+0

Tendré que recuperar mi afirmación anterior: MS dice que un par Dispose/Finalize está bien. Personalmente, no me gusta este enfoque por las razones que se detallan a continuación, pero ktrauberman ha encontrado una referencia que demuestra que no debería ser tan inflexible en este punto ... así que me rindo. –

Respuesta

11

Si implementa IDisposable correctamente, también debe incluir un finalizador que llamará a Dispose() en su objeto.

Si lo hace, el GC lo llamará. Sin embargo, sigue siendo una MUY buena idea intentar deshacerse de estos objetos por su cuenta.

El mayor problema al confiar en el finalizador para llamar a Dispose es que sucederá en otro hilo que no controlas. Esto puede tener consecuencias desagradables en ciertas situaciones, como causar una excepción en el hilo del GC, lo que no es bueno, así como tener un campo descartado que controle. Esto también es parte de por qué es importante incluir GC.SuppressFinalize (esto) en su método Dispose(): una vez que un objeto está dispuesto, no desea volver a eliminarlo.

+0

Disculpe, Mark, pero la recolección de basura C# no es la varita mágica que está implicando. Si un objeto implementa IDispose, debe disponerlo explícitamente, preferiblemente mediante el uso. Sin embargo, su consejo es correcto para otros objetos, ¡pero los recursos que no son de memoria son peligrosos! –

+0

Si el usuario nunca llama a Dispose(), y no usa un bloque de uso, los recursos no administrados nunca se limpiarán. Aquí depende de por qué su clase implementa IDiposable, pero la pregunta explícitamente dice "para desasignar recursos no administrados". Los finalizadores existen por exactamente esta razón. –

+0

No estoy seguro de que estemos en desacuerdo con Pontus; Creo clases IDisposable específicamente para usar en un bloque Using. De lo contrario, debería llamarlo explícitamente para liberar recursos no administrados, liberar conexiones, etc. –

2

Dispose() se llama al final del bloque de Uso para que pueda contar con las acciones de Dispose (por ejemplo, cerrar una conexión de db) a medida que el objeto sale del alcance. Simple como eso.

Actualización: no hay nada específico para recursos no administrados en una llamada a Dispose (aunque ese es un uso común).

Actualización 2: hay un poco de debate sobre el hilo que comenzó Reed Copsey que es útil para entender IDisposable. Recomiendo encarecidamente this article para las personas que quieran saber más.

En pocas palabras, una clase IDisposable le permite manejar explícitamente la desasignación de recursos (normalmente recursos no administrados o conexiones de bases de datos) a través del método Dispose(). Las instancias de clase IDisposable deben crearse dentro de un bloque "Using" para asegurar que el método Dispose sea realmente llamado. Si no lo hace (o lo llama explícitamente en un bloque "final", etc.), entonces no se llamará a su método Dispose y usted huérfano de los objetos que desea limpiar. En todos los casos, coloco clases Desechables en Usar bloques y usted también debería.

Como alternativa puede gestionar la limpieza de recursos en un Finalizer (un Destructor de clase). Esto se llamará automáticamente cuando la clase sea GC'd. Las desventajas de este enfoque son que no se controlará explícitamente cuando se limpian los objetos y hay algunos problemas de subprocesamiento que enfrentar. Por lo tanto, por ejemplo, los problemas en Destructors son muy difíciles de depurar debido al hecho de que se llaman asíncronamente y en un hilo diferente. La única ventaja es que no tiene que recordar llamar a su Destructor como lo hace. Por supuesto, si siempre usa el uso de bloques, esto no es un problema.

NOTA: I ktrauberman, Reed y Pontus han hecho algunos buenos comentarios acerca de cómo puede solucionar los puntos que hago a continuación. Esto es lo que hago, pero no puedo argumentar que esta es la única forma de hacer las cosas. De hecho, Microsoft incluso recomienda llamar a Dispose() desde su Finalizer en algunos casos. Sin embargo, dejaré la discusión aquí solo como una ilustración de por qué es importante seguir los consejos de Reed sobre: ​​tener cuidado al mezclar Destructores y Eliminar().

En lo que no estoy de acuerdo con la respuesta de Reed es en la afirmación de que debe implementar una clase IDisposable llamando a Dispose() dentro de su Finalizer. Esto simplemente combina los dos constructos diferentes y puede conducir a problemas. Por ejemplo, si crea su instancia de clase IDisposable en un bloque de Uso y llama a Dispose() en el Destructor, se llamará dos veces - con posibles errores desagradables y difíciles de depurar (nuevamente - usted no controla el tiempo de GC). (Nota al margen: esto es realmente cierto de destructores en general - que pueden, en ciertas circunstancias pueden llamar más de una vez!)

+0

Mark, el artículo que enlazas contradice tu posición. Dice en la página 3 "Microsoft recomienda que implemente Dispose y Finalize cuando trabaje con recursos no administrados". En la página 4, también muestra un ejemplo en el que llama a Dispose() desde el destructor. –

+0

No creo que comprenda completamente lo que GC.SupressFinalize() hace. Usted llama esto al final de su método Dispose(), por lo que si el desarrollador llama a Dispose() el recolector de basura nunca ejecuta el finalizador, por lo que la limpieza solo se produce una vez. –

+0

kt - gracias por el debate. No mezclo los dos pero, como dije antes, tengo que admitir tu punto y hacerlo con gusto. Los debates sobre SO siempre son buenos para empujar el sobre de lo que sabemos. –

2

Desechar se llama en algunos lugares:

  1. Al final de un uso bloquear.
  2. Cuando se llama explícitamente (en un try {} finally {} por ejemplo.)

Se recomienda llamarlo usted mismo cuando haya terminado con un recurso, para administrar mejor los recursos.

EDIT: Estaba equivocado. Dispose NO se llama durante la recolección de basura. Vea el artículo this.

+0

Dispose() no se llama automáticamente durante la recolección de basura. –

+0

+1 por el comentario de Brian. –

+0

Acaba de editarse. Hice algunas investigaciones después de publicar (que debería haber hecho de antemano) mi mal. –

2

No, no se consiga llamar cuando el objeto es basura recogida. Si desea ese comportamiento, puede usar el destructor (finalizador) y la llamada Dispose() desde allí.

Como dices, se llama automáticamente y al final de un bloque using.

Cuestiones relacionadas