2010-10-21 23 views
10

me hizo una question acerca de este método:¿Cuándo desechar y por qué?

// Save an object out to the disk 
public static void SerializeObject<T>(this T toSerialize, String filename) 
{ 
    XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType()); 
    TextWriter textWriter = new StreamWriter(filename); 

    xmlSerializer.Serialize(textWriter, toSerialize); 
    textWriter.Close(); 
} 

en la respuesta que me dieron esto como una observación añadida:

asegurarse de que siempre eliminar Recursos desechables tales como arroyos y lectores de texto y escritores. Este no parece ser el caso en su método SerializeObject.

Por lo tanto, puedo decir que esto va a parecer supercoco para alguien que ha estado codificando C# durante un año o dos, pero ¿por qué tengo que deshacerme de él?

Se ve que testWriter tiene un método de eliminación, pero ¿no debería la recolección de basura ocuparse de eso? Vine de Delphi a C#. En Delphi tuve que limpiar todo, así que este no es un caso de mí que quiera ser flojo. Me acaban de decir que si fuerza a liberar la memoria que toman sus objetos, entonces puede causar problemas. Me dijeron que "Simplemente deja que el recolector de basura lo haga".

  1. Entonces, ¿por qué debo llamar a deshacerme? (Mi suposición es que es porque textWriter toca el disco.)
  2. ¿Hay una lista de objetos con los que debo tener cuidado? (¿O una manera fácil de saber cuándo debo llamar a deshacerse?)
+1

Relacionado: http://stackoverflow.com/questions/1691846/does-garbage-collector-call-dispose –

+0

Tengo entendido que hace que el GC sepa que esto está listo para la recolección, en lugar de tener que verificar si está listo.Además, puede decirle al GC que recopile "temprano" este elemento, en lugar de que el marco quede colgado por un tiempo "por las dudas" http://www.devx.com/dotnet/Article/33167 – jcolebrand

+4

@drachenstern - concepto erróneo popular, pero incorrecto. Dispose simplemente le proporciona un mecanismo para cerrar a la fuerza los recursos no administrados, como los identificadores de archivos o los sockets de red, tan pronto como haya terminado con ellos sin esperar al recolector de elementos no utilizados. No indica al GC que recoja el objeto. – Paolo

Respuesta

12

Tiene razón en que, para un código escrito correctamente, el GC eventualmente limpiará los recursos nativos. El objeto tendrá un finalizador, y durante la finalización liberará los recursos nativos necesarios.

Sin embargo, cuando esto sucede no es muy determinista. Además, está un poco retrógrado porque está utilizando el GC, que fue diseñado para manejar la memoria administrada como un medio para administrar recursos nativos.Esto conduce a casos interesantes y puede causar recursos nativos para mantenerse con vida mucho más larga de lo previsto que lleva a situaciones en las que

  • archivos están abiertos mucho después de que ya no se utilizan
  • manijas de recursos pueden agotarse debido a que el GC doesn' t ver suficiente presión de memoria para forzar una colección y, por tanto, ejecutar finalizadores

El patrón de uso/eliminación agrega un determinismo a la limpieza de los recursos nativos y elimina estos problemas.

1

Si está abriendo un recurso (como un archivo o está abriendo una conexión a una base de datos), eliminar el recurso liberará su retención del recurso . Si no lo hace, es posible que otras personas no puedan conectarse a la base de datos o usar el archivo.

Como regla general ... si la clase implementa la interfaz IDisposable, debe llamar al método Dispose() cuando la finaliza. Es más que probable que haya una razón para que sea desechable :)

2

Si está utilizando recursos nativos (por ejemplo, identificadores de archivos), debe llamar a Dispose() para cerrarlos pronto, y no cuando se ejecuta el GC (que podría ser mucho más tarde en las generaciones de mayor gc). Y desea cerrar el archivo ya que el acceso al archivo generalmente bloquea el archivo de alguna manera.

4

Si sabe que no va a utilizar un cierto recurso, puede simplemente deshacerse de él usted mismo; sin duda será más rápido que el recolector de basura y permitirá que otros usen el archivo o lo que haya abierto más rápido. La forma más fácil sería utilizar su TextWriter o cualquier otro recurso en un using:

using (TextWriter textWriter = new StreamWriter(filename)) 
{ 
    xmlSerializer.Serialize(textWriter, toSerialize); 
} 

Esto asegura básicamente la TextWriter está dispuesto en el extremo. No lo necesitas más que eso, de todos modos.

+3

probablemente no sea necesario hacer ambas cosas Cerrar y desechar –

+0

@Conrad, sí, lo sé. Dejé su código casi como estaba, solo para mostrarle que hay muy poco que necesita cambiar. – alex

4

El recolector de basura libera todos los recursos, pero el momento en que lo hace no está definido. El método Dispose proporciona una forma de liberar recursos no administrados de inmediato.

+0

"Garbage collector libera todos los recursos" No necesariamente: Imagine el objeto que Bob se ha suscrito a un evento de un objeto de mayor duración (por ejemplo, AppDomain). ¡El GC nunca limpiará a Bob a menos que le diga que se retire de ese evento! – Niki

+0

Derecha, GC solo llama a finalizador, es responsabilidad del desarrollador de clase liberar recursos en Dispose/finalizer. Estaba hablando sobre el uso de clases de .NET desechables, como TextWriter de la pregunta, que (afortunadamente) están escritas correctamente. –

15

La regla de oro aquí es bastante simple: siempre llame al Dispose() en los objetos que implementan IDisposable (no todos los objetos lo hacen). No siempre sabrá la razón por la cual un objeto tuvo que implementar Dispose, pero debe suponer que está ahí por algún motivo.

La forma más fácil de asegurarse de hacer esto es a través de using:

using (TextWriter tw = new StreamWriter(fileName)) 
{ 
    // your code here 
} 

Para ello, será Dispose() automáticamente al final del uso de bloque (que es fundamentalmente el mismo que el uso de un try/catch/finally con el Dispose() en el bloque finally).

Para obtener más información sobre cómo Desechar funciona con la recolección de basura, see here.

0

El recolector de basura de hecho "se encargará de eso". Tarde o temprano. Cuando llegue el momento de llamar al finalizador, después de la siguiente recolección de basura.

Llamar a Dispose garantiza que los recursos (como los identificadores de archivos) se liberen lo antes posible, de modo que estén disponibles para su reutilización en otros procesos del sistema. La mayoría de las veces, no notará la diferencia entre llamar al Dispose usted mismo y dejar que el recolector de basura lo maneje. Pero hay veces en que lo harás. Un rastreador web, por ejemplo, que crea muchos objetos HttpWebResponse, se quedará sin conexiones rápidamente si no los desecha después de su uso. (No es que me he encontrado con ese problema ... no, yo no).

0

La razón para llamar a Desechar en lugar de esperar al GC es a menudo porque está utilizando un recurso de sistema finito que desea liberar como lo más rápido posible para que otros procesos o hilos puedan usarlo. Para los objetos que tienen el patrón IDisposable, la construcción "using" es una forma fácil y legible de asegurarse de que se llame a Dispose.

1

De la documentación TextWriter.Dispose:

Nota Siempre llame antes de botar liberar su última referencia al TextWriter. De lo contrario, los recursos que está utilizando no se liberarán hasta que el recolector de basura llame al método Finalize del objeto TextWriter.

De la documentación Object.Finalize:

El momento exacto cuando el finalizador ejecuta durante la recogida de la basura es indefinido.Los recursos no son garantizados para ser liberados en cualquier momento específico, a menos que se llame a un método Close o un método Dispose.

y

El método Finalizar no se ejecute a finalización o podría no funcionar en absoluto en los siguientes excepcionales circunstancias:

  • Otros bloques Finalizer indefinidamente (va en un bucle infinito, intenta obtener un candado que nunca puede obtener y pronto). Como el tiempo de ejecución intenta ejecutar los finalizadores hasta su finalización, es posible que no se llamen a otros finalizadores si un finalizador bloquea indefinidamente.

  • El proceso finaliza sin dar a el tiempo de ejecución la posibilidad de limpieza. En este caso, la primera notificación de terminación de proceso del del tiempo de ejecución es una notificación DLL_PROCESS_DETACH.

El tiempo de ejecución continúa para finalizar objetos durante el apagado sólo cuando el número de objetos finalizables continúa disminuyendo.

0

En general, debe disponer de los objetos cuando ya no los necesite.

El objeto no desechable cuando termine con ellos será el que bloqueará el acceso a ellos para otras aplicaciones/procesadas.

3

Bueno, en realidad ya lo está desechando ya que lo hace el método textWriter.Close.

public virtual void Close() 
{ 
    this.Dispose(true); 
    GC.SuppressFinalize(this); 
} 

Así que podría cambiar su código a.

public static void SerializeObject<T>(this T toSerialize, String filename) 
{ 
    TextWriter textWriter; 
    try 
    { 
     XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType()); 
     textWriter = new StreamWriter(filename); 

      xmlSerializer.Serialize(textWriter, toSerialize); 
    } 
    finally 
    { 
     textWriter.Close(); 
    } 

Que es bastante similar a lo que usa el uso() en las otras respuestas.

El impacto de no hacer esto es que si ocurre un error con Serialize, pasaría un tiempo antes de que Framework dejara de funcionar su bloqueo de archivo (cuando procesa la cola fReachable).

Sé que FxCop te dice cuándo implementar IDisposable, pero no creo que haya una manera fácil de saber cuándo debes llamar a Dispose, aparte de mirar los documentos y ver si un objeto implica IDisposable (o intellisense).

0

Un programador que piensa que uno no tiene que preocuparse por deshacerse de las cosas porque un finalizador se encargará de ellas es como un conductor pensando que no tiene que preocuparse por evitar colisiones porque el automóvil tiene un airbag. Sí, un airbag hará que las cosas sean más duraderas, pero ...

+0

No es una respuesta muy útil ... –

+0

Hay muchas razones por las que un finalizador puede no ejecutarse en el momento oportuno; aunque hay algunas situaciones en las que puede no ser práctico disponer de las cosas de manera determinista (por ejemplo, porque un recurso es compartido por un número desconocido de objetos), casi cualquier programa que dependa de un finalizador para una operación correcta generalmente debe considerarse roto. – supercat

Cuestiones relacionadas