2010-10-15 25 views
8

Sé que esto puede parecer tonto, pero ¿por qué el siguiente código solo funciona si cierro() el archivo? Si no cierro el archivo, la secuencia completa no se escribe.¿Por qué debo cerrar() un archivo en C#?

Pasos:

  1. ejecuta este código en forma de carga.
  2. Cerrar formulario usando el mouse una vez que se muestra.
  3. El programa finaliza.

¿No debería enjuagarse o cerrarse automáticamente el objeto de archivo cuando se sale del alcance? Soy nuevo en C#, pero estoy acostumbrado a agregar llamadas a Close() en destructores de C++.

// Notes: complete output is about 87KB. Without Close(), it's missing about 2KB at the end. 

// Convert to png and then convert that into a base64 encoded string. 
string b64img = ImageToBase64(img, ImageFormat.Png); 
// Save the base64 image to a text file for more testing and external validation. 
StreamWriter outfile = new StreamWriter("../../file.txt"); 
outfile.Write(b64img); 
// If we don't close the file, windows will not write it all to disk. No idea why 
// that would be. 
outfile.Close(); 
+5

Usted puede encontrar la discusión en http://blogs.msdn.com/b/oldnewthing/archive/ 2010/08/10/10048150.aspx para ser útil al proporcionar una explicación y justificación para las diferencias entre C# y C++. En general, compararía los destructores C++ con C# 'IDisposable' y' using {} '. Y, por supuesto, 'using' es más seguro que llamar' Close' o 'Dispose' explícitamente, ya que envuelve la llamada en un bloque' try-finally' para asegurar que el objeto sea eliminado. – Brian

+0

Gracias, todos, por la ayuda clara y rápida. – Harvey

Respuesta

21

C# no tiene una limpieza determinista automática. Debe asegurarse de llamar a la función de limpieza si desea controlar cuándo se ejecuta. El bloque using es la forma más común de hacerlo.

Si no realiza la llamada de limpieza usted mismo, la limpieza se realizará cuando el recolector de basura decida que la memoria es necesaria para otra cosa, lo que podría ser un tiempo muy largo después.

using (StreamWriter outfile = new StreamWriter("../../file.txt")) { 
    outfile.Write(b64img); 
} // everything is ok, the using block calls Dispose which closes the file 

EDIT: Como señala Harvey, mientras que la limpieza se intentará cuando el objeto sea recolectado, esto no es ninguna garantía de éxito. Para evitar problemas con las referencias circulares, el tiempo de ejecución no intenta finalizar los objetos en el orden "correcto", por lo que el FileStream en realidad ya puede estar muerto cuando se ejecuta el finalizador StreamWriter e intenta vaciar la salida almacenada en el búfer.

Si usted trata de objetos que necesitan limpieza, lo hacen de forma explícita, ya sea con using (para el uso local de ámbito) o llamando IDisposable.Dispose (para objetos de larga vida, como referentes de miembros de la clase).

+1

Como nota adicional, cada vez que observe un objeto que implementa 'IDisposable', debe hacer uso de' using', a menos que no desee que el objeto limpiarse de inmediato (p. ej., si es una variable miembro, es posible que desee "Eliminarlo" más adelante). – Brian

+0

imagine que C++ tiene que limpiar todo ... miedo – Spooks

+0

Supongo que lo que me pilló desprevenido es que la función de limpieza ** nunca fue necesaria para mí a menos que cierre. Parece que cuando finalizo el programa, en lugar de ejecutar la limpieza en cualquier objeto que deba ser eliminado, simplemente termina sin recolección de basura. – Harvey

8

Debido Write() es amortiguada y el buffer se vacía explícitamente por Close().

+1

O mediante una llamada explícita a Flush(). –

+2

Que el identificador de archivo nativo debe cerrarse es más importante IMO. Especialmente porque esto puede bloquear el archivo hasta que finalmente se ejecute el finalizador. – CodesInChaos

1

Caché del sistema operativo escribe en dispositivos de bloque para permitir que el sistema operativo tenga un mejor rendimiento. Para forzar una escritura, se vacía el búfer después de escribir para configurar el secuenciador para que se autoflush.

+0

Todavía no cierra el archivo, y la mayoría de los demás programas no pueden abrirlo hasta que el primer programa lo cierra. –

+1

Sí, mi punto era forzar la escritura, no es necesario que cierre – rerun

3

Los flujos son objetos que "gestionan" o "manejan" recursos no recolectados como basura. Por lo tanto, ellos (Streams) implementan la interfaz IDisposable que, cuando se usa con 'using', se asegurará de que los recursos no recolectados que no son basura se limpien. intente esto:

using (StreamWriter outfile = new StreamWriter("../../file.txt")) 
{ 
    outfile.Write(b64img); 
} 

Sin la #Cerrar, no se puede estar seguro de que cuando el identificador de archivo subyacente se cerrará correctamente. A veces, esto puede ser al apagar la aplicación.

+0

Gracias, eso ayuda a aclarar algo de confusión. En mi caso, el identificador del archivo ** nunca ** se cerró, incluso al cerrar la aplicación. (Cerrado en el sentido de que se cerrará un Close() explícito) – Harvey

+0

eres bienvenido. – DevSolo

-2

Porque los diseñadores de C# clonaban Java y no C++ a pesar del nombre.

En mi opinión, realmente echaron de menos el barco. La destrucción del estilo de C++ en la salida del alcance habría sido mucho mejor.

Ni siquiera tendría que liberar la memoria para ser mejor, simplemente ejecute automáticamente el finalizador o el método IDisposable.

+2

-1: Quejarse de que el lenguaje está defectuoso no es realmente útil, especialmente las quejas como "C# es una copia de Java y es por eso que funciona como Java". Esta fue una decisión de diseño deliberada, y se relaciona con cómo funciona la recolección de basura en C#. Consulte http://blogs.msdn.com/b/oldnewthing/archive/2010/08/10/10048150.aspx para obtener una explicación de por qué la eliminación automática no funcionará. – Brian

+1

No soy el que votó negativamente, pero en 6k rep, ya deberías saber cómo dejar los comentarios. Esta no es una respuesta. Por cierto, lo que usted necesita existe, se llama C++/CLI, soporte de .NET con RAII y llamadas automáticas al destructor al final del alcance. –

+1

clase C {Stream s; void M() {S s2 = OpenAStream(); this.s = s2; }}: ¿tu idea es que la transmisión se elimine automáticamente cuando s2 se salga del alcance, aunque esto todavía tenga una referencia? ¿No crees que eso sería sorprendente para la gente? –

2

Puesto que está utilizando un StreamWriter y no vaciar el almacenamiento intermedio hasta que Close() el escritor. Puede especificar que desea que el escritor descargue cada vez que llame a escritura estableciendo la propiedad AutoFlush del editor de secuencia en verdadero.

Revisa los documentos. http://msdn.microsoft.com/en-us/library/system.io.streamwriter.aspx

Si desea escribir en un archivo sin "cierre", me gustaría utilizar:

System.IO.File 
Cuestiones relacionadas