net - ¿Cuál es la diferencia entre usar IDisposable vs un destructor en C#?
implementar dispose (7)
Aquí hay otro artículo fino que aclara algo de la niebla que rodea el IDisposable, el GC y lo elimina.
¿Cuándo implementaré IDispose en una clase en lugar de un destructor? Leí este artículo , pero sigo sin entender el punto.
Mi suposición es que si implemento IDispose en un objeto, puedo "destruirlo" explícitamente en lugar de esperar a que el recolector de basura lo haga. ¿Es esto correcto?
¿Eso significa que siempre debería llamar explícitamente a Dispose en un objeto? ¿Cuáles son algunos ejemplos comunes de esto?
Es bastante simple realmente. Sé que ha sido respondida, pero intentaré de nuevo, pero trataré de mantenerlo lo más simple posible.
Un destructor generalmente nunca debe ser usado. Solo se ejecuta .net quiere que se ejecute. Solo se ejecutará después de un ciclo de recogida de basuras. Es posible que en realidad nunca se ejecute durante el ciclo de vida de su aplicación. Por esta razón, nunca deberías poner ningún código en un destructor que ''deba'' ejecutarse. Tampoco puede confiar en que existan objetos existentes dentro de la clase cuando se ejecutan (es posible que ya se hayan limpiado ya que el orden en que se ejecutan los destructores no está garantizado).
IDisposible se debe usar siempre que tenga un objeto que crea recursos que necesitan limpieza (es decir, archivos y manejadores de gráficos). De hecho, muchos argumentan que todo lo que pongas en un destructor debe ponerse IDisposable debido a las razones enumeradas anteriormente.
La mayoría de las clases llamarán a deshacerse cuando se ejecute el finalizador, pero esto es simplemente como una protección segura y nunca se debe confiar en él. Debería eliminar explícitamente todo lo que implemente IDisposable cuando haya terminado con él. Si implementa IDisposable, debe llamar a dispose en el finalizador. Consulte MSDN para ver un ejemplo.
Hay una muy buena descripción en MSDN :
El uso principal de esta interfaz es liberar recursos no administrados . El recolector de basura libera automáticamente la memoria asignada a un objeto administrado cuando ese objeto ya no se usa. Sin embargo, no es posible predecir cuándo ocurrirá la recolección de basura . Además, el recolector de basura no tiene conocimiento de los recursos no administrados , como los identificadores de ventanas o los archivos y las secuencias abiertos .
Utilice el método Dispose de esta interfaz para liberar explícitamente recursos no administrados junto con el recolector de elementos no utilizados. El consumidor de un objeto puede llamar a este método cuando el objeto ya no se necesita.
La función del método Finalize()
es garantizar que un objeto .NET pueda limpiar recursos no administrados cuando se recolecta basura . Sin embargo, los objetos como las conexiones de bases de datos o los manejadores de archivos se deben liberar lo antes posible, en lugar de confiar en la recolección de basura. Para eso debes implementar IDisposable
interfaz IDisposable
y liberar tus recursos en el método Dispose()
.
Lo único que debería estar en un destructor de C # es esta línea:
Dispose(False);
Eso es. Nada más debería estar en ese método.
Su pregunta con respecto a si debe o no llamar siempre a Dispose
generalmente es un acalorado debate. Consulte this blog para obtener una perspectiva interesante de personas respetadas en la comunidad .NET.
Personalmente, creo que la posición de Jeffrey Richter de que llamar a Dispose
no es obligatorio es increíblemente débil. Él da dos ejemplos para justificar su opinión.
En el primer ejemplo, dice que llamar a Dispose
en los controles de Windows Forms es tedioso e innecesario en los escenarios convencionales. Sin embargo, no menciona que Dispose
realmente se llama automáticamente por los contenedores de control en esos escenarios principales.
En el segundo ejemplo, él indica que un desarrollador puede asumir incorrectamente que la instancia de IAsyncResult.WaitHandle
debe eliminarse agresivamente sin darse cuenta de que la propiedad inicializa de forma lenta el identificador de espera, lo que resulta en una penalización de rendimiento innecesaria. Sin embargo, el problema con este ejemplo es que IAsyncResult
sí mismo no se adhiere a las propias directrices publicadas de Microsoft para tratar con objetos IDisposable
. Es decir, si una clase contiene una referencia a un tipo IDisposable
, la clase en sí debería implementar IDisposable
. Si IAsyncResult
siguió esa regla, entonces su propio método Dispose
podría tomar la decisión con respecto a cuál de sus miembros constituyentes necesita deshacerse.
De modo que, a menos que alguien tenga un argumento más convincente, me quedaré en el campo "siempre llame a tirar", con la certeza de que habrá algunos casos marginales que surgen principalmente de malas elecciones de diseño.
Un finalizador (también conocido como destructor) es parte de la recolección de basura (GC); es indeterminado cuando (o incluso si) sucede esto, ya que el GC ocurre principalmente como resultado de la presión de la memoria (es decir, necesita más espacio). Los finalizadores generalmente solo se usan para limpiar recursos no administrados , ya que los recursos gestionados tendrán su propia recolección / eliminación.
De ahí que IDisposable
se utilice para limpiar determi- IDisposable
objetos, es decir, ahora. No recoge la memoria del objeto (que aún pertenece a GC), pero se usa, por ejemplo, para cerrar archivos, conexiones de bases de datos, etc.
Hay muchos temas anteriores sobre esto:
Finalmente, tenga en cuenta que no es raro que un objeto IDisposable
también tenga un finalizador; en este caso, Dispose()
normalmente llama a GC.SuppressFinalize(this)
, lo que significa que GC no ejecuta el finalizador, simplemente tira la memoria (mucho más barato). El finalizador aún se ejecuta si olvida Dispose()
el objeto.