2009-09-26 14 views
8

¿Puedo confiar en que un objeto se destruirá y se llama inmediatamente a su destructor cuando se sale del alcance en C#?C# - ¿Los objetos se destruyen inmediatamente cuando salen del alcance?

Creo que debido a que muchas prácticas comunes de codificación (por ejemplo, objetos de transacción) dependen de este comportamiento, no estoy muy acostumbrado a trabajar con la recolección de elementos desechados y tengo poca información sobre cómo se comportan normalmente dichos lenguajes.

Gracias.

+0

@RA - lo siento por el retraso, tenían alguna IRL tareas pendientes :) Agregué algunos ejemplos ahora, espero que esto aclare aún más. –

Respuesta

24

No, .Net y hense C# se basan en una gestión de memoria de recolección de basura. Por lo tanto, los destructores (que en .Net se llaman finalizadores) no se invocan hasta que GC considere apropiado destruir los objetos.

Además: la mayoría de los objetos "regulares" en C# no tienen destructores. Si necesita el patrón de destructor, debe implementar el IDisposable interface con el Dispose Pattern. En objetos desechables también debe asegurarse de que se llame al método Dispose, ya sea con el using keyword o llamando directamente al método.

Para aclarar (con suerte): la eliminación determinista es útil en .Net, p. cuando necesite liberar recursos explícitamente que no estén administrados por el tiempo de ejecución .Net. Ejemplos de tales recursos son manejadores de archivos, conexiones de bases de datos, etc. Por lo general, es importante que estos recursos se liberen tan pronto como ya no se necesiten. Por lo tanto, no podemos permitirnos esperar a que el CG los libere.

Para obtener una eliminación determinista (similar al comportamiento del alcance de C++) en el mundo no determinista del .Net GC, las clases .Net se basan en la interfaz IDisposable. Préstamos del Dispose Pattern, he aquí algunos ejemplos:

En primer lugar, crear instancias de un recurso desechable y luego dejar que el objeto vaya fuera de alcance, lo dejará hasta la GC para disponer del objeto:

1. { 
2.  var dr = new DisposableResource(); 
3. } 

Para Solucionar este que puede disponer de forma explícita el objeto:

1. { 
2.  var dr = new DisposableResource(); 
3. 
4.  ... 
5. 
6.  dr.Dispose(); 
7. } 

Pero lo que si algo va mal entre la línea 2 y 6? Eliminar no se llamará. Para garantizar aún más que botar finalmente se llamará independientemente de las excepciones que podemos hacer lo siguiente:

1. var dr = new DisposableResource(); 
2. try 
3. { 
4.  ... 
5. } 
6. finally 
7. { 
8.  dr.Dispose(); 
9. } 

Dado que este patrón es a menudo necesaria, C# incluye el uso de palabras clave para simplificar las cosas. El siguiente ejemplo es equivalente al anterior:

1. using (var dr = new DisposableResource()) 
2. { 
3.  ... 
4. } 
+1

La pregunta asker no conoce las sutilezas de lo que es un destructor C#, así que no creo que deba simplemente llamar a un destructor un finalizador y terminarlo. Los destructores C++ y los destructores C# son * cosas muy diferentes. * – Joren

+1

También tenga en cuenta que .NET Garbage Collection está basado en "generación", por lo que un objeto puede ser recogido basura por una colección Generación 0, pero no ha sido completamente "destruido". "o" eliminado "de la memoria. ¡También piense en cosas tales como Referencias Débiles por medio de las cuales un objeto puede ser recogido basura, pero todavía accesible por el código de su aplicación! Básicamente, un objeto durará tanto como el tiempo de ejecución lo desee. ¡No tienes voz en el asunto! ;) (http://www.simple-talk.com/dotnet/.net-framework/understanding-garbage-collection-in-.net/) – CraigTP

+0

@Peter: Gracias por su respuesta. ¿Sugiere que IDisposable ofrezca una forma de lograr el comportamiento de alcance visto, por ejemplo, en C++? ¿Consideraría elaborarlo y tal vez agregar algún código? – sharkin

0

No creo que deba confiar en los recolectores de basura de esta manera. Incluso si deduces cómo operan, bien podría ser que en el próximo lanzamiento lo hayan reimplantado.

En cualquier caso, los objetos no se recogen como basura en el momento en que los deshabilita. Por lo general, se recopilan hasta que se alcanza un umbral y luego se liberan.

Especialmente en los programas Java esto es muy notable cuando observa el consumo de memoria en el administrador de tareas. Crece y crece y, de repente, cada minuto cae nuevamente.

4

No existe tal cosa como un destructor similar a C++ en C#.(Existe un concepto diferente de destructor en C#, también llamado finalizador, que usa la misma sintaxis que los destructores de C++, pero no están relacionados con la destrucción de objetos. Están destinados a proporcionar un mecanismo de limpieza para recursos no administrados). La basura el recolector limpiará objetos en algún momento después de que ya no se haga referencia a. No de inmediato, y tampoco hay forma de garantizar esto.

Afortunadamente, tampoco existe una razón real por la que desee garantizar esto. Si necesita la memoria, entonces la GC la reclamará. Si no lo hace, ¿por qué importa si todavía hay algún objeto de basura alrededor? No es una pérdida de memoria: el GC aún puede encontrarlo y limpiarlo en cualquier momento.

4

No, esto no está garantizado. De forma similar a los lenguajes como Java, en C# el recolector de basura se ejecuta cuando es necesario (es decir, cuando el montón se está llenando demasiado). Sin embargo, cuando sus objetos implementan IDisposable, yo. mi. tienen un método Dispose() y tiene que ser llamado, entonces usted puede tomar ventaja de la palabra clave using:

using (var foo = new DisposableObject()) { 
    // do something with that 
} 

De esa manera Dispose() se llamará inmediatamente al salir que using bloque.

Nota: IDisposable se encuentra en muchos tipos, más notablemente en GDI + pero también en conexiones de base de datos, transacciones, etc. por lo que puede ser el patrón correcto aquí.

Nota 2: Detrás de las escenas anteriores bloque se traducen en un bloque try/finally:

var foo = new DisposableObject(); 
try 
{ 
    // do something with that 
} 
finally 
{ 
    foo.Dispose(); 
} 

Pero que la traducción es realizada por el compilador y muy práctico para no olvidarse de llamar Dispose().

0

No. Si se refiere a la especificación CLI (p. 8.9.6.7 acerca finalizadores) http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-335.pdf se puede encontrar la siguiente

la CLI debe asegurar que los finalizadores se llaman poco después de la instancia se convierte en inaccesible . Mientras que confían en la presión de memoria a finalización de disparo es aceptable, los ejecutores deben considerar el uso de métricas adicionales

pero no debe.

7

No. Un objeto en realidad no está "fuera del alcance", la referencia al mismo (es decir, la variable que utiliza para acceder a él) lo hace.

Una vez que no hay más referencias a un objeto dado, ese objeto se convierte en elegible para la recolección de basura (GC) si fuera necesario. Siempre que el GC decida que necesita reclamar el espacio al objeto que ya no hace referencia, es cuando se llamará al finalizador de objetos.

Si su objeto es un recurso (por ejemplo, un identificador de archivo, conexión de base de datos), debe implementar la interfaz IDisposable (que obliga al objeto a implementar un método Dispose() para limpiar cualquier conexión abierta, etc.).La mejor práctica para usted en este caso sería crear el objeto como parte de un bloque using, de modo que cuando este bloque se complete, su aplicación llamará automáticamente al método de los objetos Dispose(), que se encargará de cerrar su conexión de archivo/db /lo que sea.

p. Ej.


using (var conn = new DbConnection()) 
{ 
    // do stuff with conn 
} // conn.Dispose() is automatically called here. 

El bloque using es sólo un poco de azúcar sintáctica que envuelve sus interacciones con el objeto conn en un bloque try, junto con un bloque finally que sólo llama conn.Dispose()

Cuestiones relacionadas