29

Suppose this C# code:

using (MemoryStream stream = new MemoryStream())
{
    StreamWriter normalWriter = new StreamWriter(stream);
    BinaryWriter binaryWriter = new BinaryWriter(stream);

    foreach(...)
    {
        binaryWriter.Write(number);
        normalWriter.WriteLine(name); //<~~ easier to reader afterward.
    }

    return MemoryStream.ToArray();
}

My questions are:

  1. Do I need to use flush inside the loop to preserve order?
  2. Is returning MemoryStream.ToArray() legal? I using the using-block as a convention, I'm afraid it will mess things up.
H. Pauwelyn
  • 13,575
  • 26
  • 81
  • 144
Nefzen
  • 7,819
  • 14
  • 36
  • 34

2 Answers2

26

Scratch the previous answer - I hadn't noticed that you were using two wrappers around the same stream. That feels somewhat risky to me.

Either way, I'd put the StreamWriter and BinaryWriter in their own using blocks.

Oh, and yes, it's legal to call ToArray() on the MemoryStream - the data is retained even after it's disposed.

If you really want to use the two wrappers, I'd do it like this:

using (MemoryStream stream = new MemoryStream())
{
    using (StreamWriter normalWriter = new StreamWriter(stream))
    using (BinaryWriter binaryWriter = new BinaryWriter(stream))
    {
        foreach(...)
        {
            binaryWriter.Write(number);
            binaryWriter.Flush();
            normalWriter.WriteLine(name); //<~~ easier to read afterward.
            normalWriter.Flush();
        }
    }    
    return MemoryStream.ToArray();
}

I have to say, I'm somewhat wary of using two wrappers around the same stream though. You'll have to keep flushing each of them after each operation to make sure you don't end up with odd data. You could set the StreamWriter's AutoFlush property to true to mitigate the situation, and I believe that BinaryWriter currently doesn't actually require flushing (i.e. it doesn't buffer any data) but relying on that feels risky.

If you have to mix binary and text data, I'd use a BinaryWriter and explicitly write the bytes for the string, fetching it with Encoding.GetBytes(string).

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Wrapping one stream with two stream writers does seem dangerous/error prone though I don't think flushing them will do anything in this case. Flush doesn't affect the reading of the MemoryStream, only the writing of the buffered bytes into their respective streams. – mbillard Jun 29 '09 at 16:28
  • 2
    It affects the order in which the values may be written though - unless you set AutoFlush to true, StreamWriter may not write anything to the MemoryStream for a long time (or *ever* if you don't dispose it). It just feels brittle to me. – Jon Skeet Jun 29 '09 at 16:34
  • the thing is, I want a newline at the end of the the name, and WriteLine seems the safe way to so so. – Nefzen Jun 29 '09 at 16:35
  • "Flush doesn't affect the reading of the MemoryStream, only the writing of the buffered bytes into their respective streams" .... but that's exactly what the OP is doing... writing. – dss539 Jun 29 '09 at 16:36
  • 2
    @Nefzen - see Environment.NewLine http://msdn.microsoft.com/en-us/library/system.environment.newline.aspx – dss539 Jun 29 '09 at 16:37
  • @dss539 - but will that be read when using readline every time? – Nefzen Jun 29 '09 at 16:39
  • @Nefzen: Using ReadLine is going to be dodgy with a StreamReader anyway, if you're mixing it with binary data. ReadLine will read into an internal buffer, consuming some of that binary data. Mixing text and binary data without a well-defined container (e.g. length-prefixing the text having indicated that there *will* be text) is always tricky. – Jon Skeet Jun 29 '09 at 17:00
  • binary data is constant in length - 32 bit. I came at a new problem - in the other side, I want to read the line, yes? well, if I use StreamReader, I'm not sure what encoding it is using, and if I use BinaryReader, I need to find the end of line. Hmm, maybe I should use '\0'. – Nefzen Jun 29 '09 at 17:09
  • @dss539 the buffer will be filled in the correct order so flushing or not, the bytes will be written in the correct order. – mbillard Jun 29 '09 at 17:26
0

Update

Nevermind this answer, I got confused with the writers...


  1. No, the order will be preserved (update: maybe not). Flush is useful/needed in other situations, though I can't remember when.
  2. I think so, using makes sure everything cleans up nicely.
mbillard
  • 38,386
  • 18
  • 74
  • 98
  • I don't think the order *will* necessarily be preserved, between the binary writer and the stream writer here - you may end up with a load of values from the binary writer and then the next buffer's worth of the stream writer. – Jon Skeet Jun 29 '09 at 16:25
  • 1. No the order won't be preserved. 2. No the `using` does not `Dispose()` normalWriter or binaryWriter. – dss539 Jun 29 '09 at 16:39
  • Both writers share the same buffer? – mbillard Jun 29 '09 at 17:27
  • @Crossbrowser: No - the BinaryWriter doesn't know about the StreamWriter or vice versa. The StreamWriter has its own buffer. – Jon Skeet Jun 29 '09 at 18:10
  • Dam, I was so confused, I kept focusing on the writer part not thinking that one of the writers took his source from what the other wrote in the stream. In that case, I think only the first flush is necessary. – mbillard Jun 29 '09 at 18:38