2

except for the comments here ( MemoryStream disables reading when returned ), I haven't found a good answer to my concerns about leaving a StreamWriter open. I would like to return a MemoryStream (open) that is written by a StreamWriter in a safe and clean way.

Let me give a rather simple example. I would like to use the Method GetMemoryStream() like this:

        String[] content = { "large", "string", "array", "test" };
        MemoryStream ms = GetMemoryStream(content);
        //byte[] array = ms.ToArray(); //still works after stream is closed but I want an open and working stream.
        using (FileStream fs = new FileStream(@"C:\Test.txt", FileMode.Create, FileAccess.ReadWrite))
        {
            //fs.Write(array, 0, array.Length); 
            ms.WriteTo(fs);
        }
        ms.Dispose();

Now here are two ways I found to do so:

  1. Copy the MemoryStream

    public static MemoryStream GetMemoryStream(String[] content)
    {
        MemoryStream result = new MemoryStream();
        using (MemoryStream ms = new MemoryStream())
        using (StreamWriter sw = new StreamWriter(ms))
        {
            foreach (String s in content)
                sw.WriteLine(s);
            sw.Flush();
            ms.CopyTo(result);
        }
        return result;
    }
    

This aproach looks like a workaround to me. It seems (correct me if I'm wrong) to need twice as much memory and more time. Then I read about the next aproach:

  1. Leave the StreamWriter Open (>=.Net 4.5)

    public static MemoryStream GetMemoryStream(String[] content)
    {
         MemoryStream ms = new MemoryStream();
         using (StreamWriter sw = new StreamWriter(ms, Encoding.Default, 1024, true))
         {
              foreach (String s in content)
                   sw.WriteLine(s);
         }
         return ms;
    }
    

But I have concerns with that aproach:

What happens to the StreamWriter when I leave the scope?
Is the data still referenced/held by that StreamWriter object?
Will it be garbage-collected? Maybe even before I get to use the MemoryStream in the calling scope?
Is it still "connected" to the MemoryStream, and maybe disposed when I dispose ms?

To sum it up: Is the second aproach going to cause any trouble?

Thanks in advance

Jan
  • 333
  • 2
  • 15
  • 1
    You are correctly using `using` in the second approach. You should not be worried about these things when you are correctly using `using`. This is why `using` exists in the first place. – GSerg Oct 05 '18 at 10:06
  • Go through the link https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose to understand about disposal in depth. – Sham Oct 05 '18 at 10:09
  • 1
    @HansPassant - "disposing the StreamWriter disposes the MemoryStream as well" - when they're already passing `true` as the `leaveOpen` parameter to the `StreamWriter` constructor? – Damien_The_Unbeliever Oct 05 '18 at 10:17
  • 1
    The second piece of code is correct, it will leave the underlying stream undisposed because of the `leaveOpen` parameter being `true`. – Lasse V. Karlsen Oct 05 '18 at 10:18
  • Thanks for the quick replies. @Hans Passant, I am already using the ctor that leaves the memorystream open. But I want it to get closed after I dispose the returned MemoryStream. Not to early though... I am afraid the StreamWriter will get garbage collected before using/disposing the MemoryStream. – Jan Oct 05 '18 at 10:19
  • If the `StreamWriter` is collected this will not impact the `MemoryStream` at all since there is no finalizer in `StreamWriter`. Additionally, `MemoryStream` is a managed reference which such a finalizer shouldn't touch anyway. – Lasse V. Karlsen Oct 05 '18 at 10:20
  • So, the disposal of the StreamWriter (with "leave open") will just "detach" the underlying MemoryStream, leaving no reference to the StreamWriter.(?) Thats what I wanted to here. :D (I will read further into the GC, thanks for the link) – Jan Oct 05 '18 at 10:25
  • The `MemoryStream` knows nothing about the `StreamWriter`, regardless of what you pass for `leaveOpen` or which constructor on `StreamWriter` you use. – Lasse V. Karlsen Oct 05 '18 at 10:56
  • Got it now, thanks. In the end I was confused by MemoryStream.Close() done by StreamWriter.Dispose(). – Jan Oct 05 '18 at 10:59
  • And you don't need the sw.Flush(). That too is taking care of by the `using{}`. – bommelding Oct 05 '18 at 11:53
  • Thanks @bommelding, that might be true, can you post a link with prove? I always wanted to see stuff like that in the docs, but I don't know where to look. [link](https://learn.microsoft.com/de-de/dotnet/api/system.io.streamwriter.dispose?redirectedfrom=MSDN&view=netframework-4.7.2) – Jan Oct 05 '18 at 12:03
  • It's just a few clicks from that german page you linked to. But it is spread out over about 3 pages. – bommelding Oct 05 '18 at 12:10
  • "A few clicks away"?(... To the left or to the right?) If you got the link would you please be so kind and post it? Sorry for the german link, google/browser/site decides the language, I gave up trying... you could "disable german" with a "slide switch" at the top-right of that page. – Jan Oct 05 '18 at 12:39
  • You go from StreamWiter.Dispose to Stream.Dispose to Stream.Close. – H H Oct 06 '18 at 17:29
  • Thanks, I could find the answer in Stream.Flush(). As for Stream.Close() they write stuff about flushing in common but there is not explit "Close will flush"-Sentence. I will edit answer and question now. – Jan Oct 08 '18 at 15:00

2 Answers2

1

So after reading the comments of my Question, its safe to use this aproach:

String[] content = { "large", "string", "array", "test" };
MemoryStream ms = GetMemoryStream(content);
using (FileStream fs = new FileStream(@"C:\Test.txt", FileMode.Create, FileAccess.ReadWrite))
{
   ms.WriteTo(fs);
}
ms.Dispose();

--

public static MemoryStream GetMemoryStream(String[] content)
{
    MemoryStream ms = new MemoryStream();
    using (StreamWriter sw = new StreamWriter(ms, Encoding.Default, 1024, true))
    {
        foreach (String s in content)
            sw.WriteLine(s);
    }
    return ms;
}

My concerns that there will be "leftovers" (references, blocking-memory, ...) of that StreamWriter were wrong. The StreamWriter gets fully disposed, but it wont touch/dispose the underlying MemoryStream when using that ctor. (please correct me when I am still wrong.)

Thanks to all

Jan
  • 333
  • 2
  • 15
  • When you're "nesting" streams, it's normal (as here) that the inner stream has *no knowledge* of which streams have been wrapped around it. The `StreamWriter` has a reference to the `Stream` you passed in the constructor (not knowing what type of stream it even is). There aren't references in the other direction. – Damien_The_Unbeliever Oct 05 '18 at 10:46
  • Yes thanks. I guess what troubled my tired brain in the first place was that the disposal of StreamWriter also closed the underlying Stream. I thought they were somehow "bound" to each other, but its just "bad" design of the StreamWriter Class then, isn't it? – Jan Oct 05 '18 at 10:50
  • in a lot of cases (mostly streams other than memory streams) it's the right thing to do; So I don't think it's a bad *default* and at least it's changeable. – Damien_The_Unbeliever Oct 05 '18 at 10:52
  • As long as its possible to prevent the close() I agree. But afaik that was not possible before .Net4.5. (You could maybe inherit StreamWriter to prevent that, but when thats not possible ... I am back to my first aproach) Either way, thanks for the input. =) – Jan Oct 05 '18 at 10:56
0

A using scope is a syntactic sugar of try-finally block, equal to the code below, Dispose method is implicitly called.

StreamWriter sw;
try
{
    StreamWriter sw = new StreamWriter(ms, Encoding.Default, 1024, true);
    foreach (String s in content)
        sw.WriteLine(s);
}
finally
{
    if(sw != null)
        sw.Dispose();
}
shingo
  • 18,436
  • 5
  • 23
  • 42
  • Thats true, but didn't quite match my (maybe not well formed/precise) question. – Jan Oct 05 '18 at 10:29