Harvey
Harvey

Reputation: 5821

Why must I Close() a file in C#?

I know this might seem silly, but why does the following code only work if I Close() the file? If I don't close the file, the entire stream is not written.

Steps:

  1. Run this code on form load.
  2. Close form using mouse once it is displayed.
  3. Program terminates.

Shouldn't the file object be flushed or closed automatically when it goes out of scope? I'm new to C#, but I'm used to adding calls to Close() in C++ destructors.

// 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();

Upvotes: 10

Views: 11494

Answers (6)

Ben Voigt
Ben Voigt

Reputation: 283614

C# doesn't have automatic deterministic cleanup. You have to be sure to call the cleanup function if you want to control when it runs. The using block is the most common way of doing this.

If you don't put in the cleanup call yourself, then cleanup will happen when the garbage collector decides the memory is needed for something else, which could be a very long time later.

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

EDIT: As Harvey points out, while the cleanup will be attempted when the object gets collected, this isn't any guarantee of success. To avoid issues with circular references, the runtime makes no attempt to finalize objects in the "right" order, so the FileStream can actually already be dead by the time the StreamWriter finalizer runs and tries to flush buffered output.

If you deal in objects that need cleanup, do it explicitly, either with using (for locally-scoped usage) or by calling IDisposable.Dispose (for long-lived objects such as referents of class members).

Upvotes: 24

EJC
EJC

Reputation: 2141

Because you are using a streamwriter and it doesn't flush the buffer until you Close() the writer. You can specify that you want the writer to flush everytime you call write by setting the AutoFlush property of the streamwriter to true.

Check out the docs. http://msdn.microsoft.com/en-us/library/system.io.streamwriter.aspx

If you want to write to a file without "closing", I would use:

System.IO.File

Upvotes: 2

Zan Lynx
Zan Lynx

Reputation: 54325

Because the C# designers were cloning Java and not C++ despite the name.

In my opinion they really missed the boat. C++ style destruction on scope exit would have been so much better.

It wouldn't even have to release the memory to be better, just automatically run the finalizer or the IDisposable method.

Upvotes: -1

DevSolo
DevSolo

Reputation: 697

Streams are objects that "manage" or "handle" non-garbage collected resources. They (Streams) therefore implement the IDisposable interface that, when used with 'using' will make sure the non-garbage collected resources are clean up. try this:

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

Without the #Close, you can not be sure when the underlying file handle will be properly closed. Sometimes, this can be at app shutdown.

Upvotes: 3

rerun
rerun

Reputation: 25495

Operating system cache write to block devices to enable the OS to have better performance. You force a write by flushing the buffer after a write of setting the streamwriter to autoflush.

Upvotes: 1

BlueDog
BlueDog

Reputation: 976

Because Write() is buffered and the buffer is explicitly flushed by Close().

Upvotes: 9

Related Questions