tags:

views:

289

answers:

6

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();
+7  A: 

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

Tim
Or by an explicit call to Flush().
Hans Passant
That the native file handle needs to be closed is more important IMO. Especially since this can lock the file until the finalizer is finally run.
CodeInChaos
+1  A: 

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.

rerun
Still doesn't close the file, and most other programs cannot open it until the first program closes it.
Ben Voigt
Yea my point was to force the write you do not have to close
rerun
+16  A: 

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).

Ben Voigt
As an aside, any time you notice an object which implements `IDisposable`, you should make use of `using` , unless you do not want to the object to be cleaned up right away (e.g. if it is a member variable, you may want to `Dispose` of it later).
Brian
imagine C++ having to clean up everything...scary
Spooks
I guess what caught me off guard is that the cleanup function is **never** called for me unless I close. It seems like when I terminate the program, instead of running cleanup on any need-to-be-disposed-of objects, it just terminates without garbage collection.
Harvey
@Spooks, yes, it is... having 10 or years of C++, is was suspicious of no 'delete' in C#. That wasn't completely unwarranted as shown in this question.
DevSolo
@Harvey: At shutdown, the runtime does try to call any remaining finalizers (at least during clean shutdown). You're seeing a different problem. There's no ordering to the cleanup of unreachable objects, internal references in an unreachable part of the object graph are completely ignored to solve circular reference problems. So what's probably happening is that the `FileStream` object owned by the `StreamWriter` got disposed first, and then when the `StreamWriter` got disposed and tried to flush its output, there was no longer anywhere to send it.
Ben Voigt
@Ben: Thanks, that was very clear and answers my question.
Harvey
+3  A: 

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.

DevSolo
Thanks, that helps clear up some confusion. In my case, the file handle was **never** closed even at app shutdown. (Closed in the sense that an explicit Close() will flush)
Harvey
you are welcome.
DevSolo
A: 

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.

Zan Lynx
-1: Complaining about the language being flawed is not really helpful, especially complains like "C# is a copy of Java and that is why it works like Java." This was a deliberate design decision, and it relates to how garbage collection works in C#. See http://blogs.msdn.com/b/oldnewthing/archive/2010/08/10/10048150.aspx for an explanation of why automatic disposal won't work.
Brian
I'm not the one who downvoted, but at 6k rep, you should know how to leave comments by now. This isn't an answer. BTW what you want does exist, it's called C++/CLI, .NET support with RAII and automatic calls to destructor at end of scope.
Ben Voigt
class C { Stream s; void M() { S s2 = OpenAStream(); this.s = s2; } } - your idea is that the stream should be disposed automatically when s2 goes out of scope, even though this.s still has a reference? Don't you think that would be surprising to people?
Eric Lippert
It may not be the answer, but it plus the comments it generated were highly informative. What if C# had met somewhere in the middle and disposed of the object when the last reference went out of scope?
Harvey
@Harvey: The problem with reference counting is circular references. So the C# language team gave up on the problem completely, instead of trying to solve the no-circular-reference case. I guess solving it only some of the time would have violated the principle of least surprise, so I don't want to disagree with their decision too strongly. Actually, I'd be pretty happy if they just made `using` blocks silently do nothing for non-disposable types instead of generating compiler errors.
Ben Voigt
@Eric: [C++/CLI troll] Well yeah, if you wanted a non-scoped Stream you'd write `Stream^ s2 = OpenAStream();` Then it wouldn't be automatically disposed. [/troll]
Ben Voigt
@Harvey: So your idea is to do a full garbage collection *every single time anything goes out of scope*? You're going to be running the garbage collector an awful lot. Or are you suggesting going to a reference-counting garbage collector and allowing circular references to become memory leaks, as they do in COM?
Eric Lippert
@Brian, @Ben: I answered the question, "Why must I close a file in C#?" Answer: "Because C# started as a clone of Java." The extra is a complaint and if I want to sacrifice some rep on a good rant, so be it.
Zan Lynx
@Eric: Could you expound at some point on why this fails to compile: http://ideone.com/W9qif Are we likely to see `using` w/runtime test for `IDisposable` added in some future version (it doesn't appear to be a breaking change)?
Ben Voigt
@Ben Voigt: Allowing code meaningless code in this manner strikes me as being confusing and ugly. I am somewhat reminded of Eric's article on when to allow redundant keywords: http://blogs.msdn.com/b/ericlippert/archive/2010/06/10/don-t-repeat-yourself-consts-are-already-static.aspx
Brian
@Brian: It's not meaningless. It's entirely possible for the object implementing `IComparable` or any other interface to also implement `IDisposable`. In fact, although `IEnumerator` doesn't inherit `IDisposable`, the C# `foreach` statement does in fact do a runtime check whether the enumerator requires disposal and does call `Dispose` whenever possible. I'm simply proposing the the `using` statement should do the same as `foreach`: a runtime type check for `IDisposable` and if it succeeds, call `Dispose`.
Ben Voigt
+2  A: 

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
EJC