24

I ran into something interesting when using a StreamWriter with a FileStream to append text to an existing file in .NET 4.5 (haven't tried any older frameworks). I tried two ways, one worked and one didn't. I'm wondering what the difference between the two is.

Both methods contained the following code at the top

if (!File.Exists(filepath))
    using (File.Create(filepath));

I have the creation in a using statement because I've found through personal experience that it's the best way to ensure that the application fully closes the file.

Non-Working Method:

using (FileStream f = new FileStream(filepath, FileMode.Append,FileAccess.Write))
    (new StreamWriter(f)).WriteLine("somestring");

With this method nothing ends up being appended to the file.

Working Method:

using (FileStream f = new FileStream(filepath, FileMode.Append,FileAccess.Write))
    using (StreamWriter s = new StreamWriter(f))
        s.WriteLine("somestring");

I've done a bit of Googling, without quite knowing what to search for, and haven't found anything informative. So, why is it that the anonymous StreamWriter fails where the (non-anonymous? named?) StreamWriter works?

Leon Newswanger
  • 617
  • 1
  • 5
  • 17
  • 2
    I'm guessing the `StreamWriter` doesn't actually _write_ anything until flushed and its `Dispose` method which gets implicitly called by using it with the `using` block will automatically flush it. EDIT: Note that the `StreamWriter` has an [AutoFlush](http://msdn.microsoft.com/en-us/library/system.io.streamwriter.autoflush.aspx) property which might control this behaviour having it auto-flush the stream whenever you write which I'm guessing is `false` by default. – Chris Sinclair Feb 02 '13 at 01:29
  • 3
    This has been answered already, but why would you want to format code like that? It's horrendous to read and understand IMO, as well as increased risk with introducing bugs through simple formatting errors. I'm a big fan of braces - always! – TheCodeKing Feb 02 '13 at 01:35
  • @TheCodeKing if it's been answered already would you mind providing a link? I honestly looked for it and looked at every suggested answer before posting and didn't see anything that quite answered it (mainly because of my use of an anonymous function.) Also, I'm not here to debate things that are for the most part stylistic and based on preference. IMO code littered with braces that aren't actually required is harder to read. – Leon Newswanger Feb 02 '13 at 01:47
  • 1
    @TheCodeKing but I also do a lot of work in Python so using indentation to determine scope has kind of become second nature. – Leon Newswanger Feb 02 '13 at 01:53
  • It's mentioned here http://msdn.microsoft.com/en-us/library/system.io.streamwriter.close.aspx, flush happens on Close if AutoFlush is not set. As Dispose closes the stream, you either need to call Dispose or Close explicitly to flush the stream. – TheCodeKing Feb 02 '13 at 12:41
  • @TheCodeKing that's not really an answer to my question though. A link to the MSDN on the `using` block would have been a better example. I'm aware that that you have to flush or dispose of a stream (which calls flush) in order for the data to be written. My question was why in the working method it was called automatically and why in the anonymous method it wasn't. That article doesn't really answer that question, which is why I asked it here. – Leon Newswanger Feb 02 '13 at 19:44
  • It's already been answered. I thought you just wanted a link to a page that mentioned the flush behaviour. You are not disposing StreamWriter in the anonymous example, hence it's not being flushed and you get no output. In the working example you are disposing the StreamWriter which closes the stream and flushes the buffer. Not sure what you're missing here? – TheCodeKing Feb 02 '13 at 21:55
  • @TheCodeKing maybe we're misunderstanding each other here. When you said "This has been answered already" were you referring to this being a duplicate question, or the fact that I've accepted an answer? – Leon Newswanger Feb 03 '13 at 03:19
  • I meant the answer below :) – TheCodeKing Feb 03 '13 at 03:47
  • @TheCodeKing Ah, okay. Then it appears that's where our misunderstanding was. I thought you were saying that this was a duplicate of another question, and I was requesting a link to that question. Sorry for the confusion. – Leon Newswanger Feb 03 '13 at 03:51

2 Answers2

21

It sounds like you did not flush the stream.

http://msdn.microsoft.com/en-us/library/system.io.stream.flush.aspx

It looks like StreamWriter writes to a buffer before writing to the final destination, in this case, the file. You may also be able to set the AutoFlush property and not have to explicitly flush it.

http://msdn.microsoft.com/en-us/library/system.io.streamwriter.autoflush.aspx

To answer your question, when you use the "using" block, it calls dispose on the StreamWriter, which must in turn call Flush.

Phillip Scott Givens
  • 5,256
  • 4
  • 32
  • 54
  • It's a bit difficult to set the AutoFlush property on an anonymous `StreamWriter`, don't you think? Unless I'm missing something. – Leon Newswanger Feb 02 '13 at 01:35
  • 2
    You should never, ever create anonymous instances of types that implement IDisposable. – xxbbcc Feb 02 '13 at 01:38
  • 2
    I wouldn't recommend using AutoFlush and not disposing the object. If an object is disposable, it should be disposed. – dtb Feb 02 '13 at 01:39
  • You are correct. You cannot set AutoFlush without assigning your class to a variable. You would have to create a code block to do that. – Phillip Scott Givens Feb 02 '13 at 01:39
  • @xxbbcc I'm aware of this and I normally don't it was one of those try and see moments and I was interested to know why it didn't work. I was assuming that because of it being anonymous it would dispose properly and didn't consider that it wouldn't flush in this method. – Leon Newswanger Feb 02 '13 at 01:40
  • @LeonNewswanger Well, as long as you're playing with the code, it's ok. :) – xxbbcc Feb 02 '13 at 01:41
  • 1
    I agree with all of the commenters who say that you should always dispose of disposable objects. Also, I wanted to reply to Leon's try-and-see comment and say that objects are never disposed unless explicitly asked to be. They are, however, finalized if they have a finalizer. – Phillip Scott Givens Feb 02 '13 at 01:43
  • @xxbbcc yeah this was a personal experiment only. I don't think my boss would appreciate such a sloppy use of an `IDisposable` object as an anonymous function. I thought the information might be useful for more practical applications. – Leon Newswanger Feb 02 '13 at 01:43
  • 2
    @PhillipScottGivens That's true but it's not good practice to rely on the finalizer. You can't know when it'll run so you don't know for how long that resource will be around. – xxbbcc Feb 02 '13 at 01:44
  • 1
    @PhillipScottGivens Not to try and bring this back after all this time but I was rereading the problem today and found that it actually is possible to set `AutoFlush` when the `StreamWriter` is initialized using `(new StreamWriter(f) {AutoFlush = true}).WriteLine("somestring")` Still not a best practice but it is another viable and working solution. – Leon Newswanger Nov 30 '13 at 16:37
  • @LeonNewswanger, I stand corrected; I had not thought to do that. kudos – Phillip Scott Givens Dec 01 '13 at 03:08
7

The difference between the two code snippets is the use of using. The using statement disposes the object at the end of the block.

A StreamWriter buffers data before writing it to the underlying stream. Disposing the StreamWriter flushes the buffer. If you don't flush the buffer, nothing gets written.

From MSDN:

You must call Close to ensure that all data is correctly written out to the underlying stream.

See also: When should I use “using” blocks in C#?

Community
  • 1
  • 1
dtb
  • 213,145
  • 36
  • 401
  • 431
  • 1
    Not to be a pain, because I was more or less assuming this already, but would it possible for you to add some references to where I could find this information online? This is as much for other's benefit as it my own. – Leon Newswanger Feb 02 '13 at 01:31