Reputation: 8240
I have some test code that's preparing a MemoryStream
that will eventually be read by an object. Here's how I want to write it:
var manager = new LeaderboardImportManager(leaderboard);
var columnNames = manager.ColumnNames;
var stream = new MemoryStream();
using (var writer = new StreamWriter(stream))
{
writer.WriteLine(string.Join(",", columnNames));
foreach (var user in users)
{
var row = leaderboard.Metrics.Select(m => Faker.RandomNumber.Next().ToString()).ToList();
row.Insert(0, user.UserName);
writer.WriteLine(string.Join(",", row));
}
writer.Flush();
stream.Position = 0;
}
return stream;
But when I do it that way, my stream object becomes unreadable and my test fails, so I have to do it like this:
var manager = new LeaderboardImportManager(leaderboard);
var columnNames = manager.ColumnNames;
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
writer.WriteLine(string.Join(",", columnNames));
foreach (var user in users)
{
var row = leaderboard.Metrics.Select(m => Faker.RandomNumber.Next().ToString()).ToList();
row.Insert(0, user.UserName);
writer.WriteLine(string.Join(",", row));
}
writer.Flush();
stream.Position = 0;
return stream;
This, of course, prevents me from being able to dispose of my StreamWriter
object, which as I understand it, should definitely be disposed of.
Why does the StreamWriter
need to remain open if I've flushed its contents to the MemoryStream
object already?
I can think of some very inconvenient ways to work around this, but I'd like to know why it doesn't work the way I want it, and whether or not there's something I can do to make it work that way. Any advice is appreciated, thanks!
Upvotes: 1
Views: 764
Reputation: 3791
using
operator calls Dispose
method on object before exiting the operator block { }
.
On disposal, StreamWriter
disposes underlying Stream
too.
This means your Stream
is an invalidated object before it gets returned.
Apply using
statement only for objects created and destroyed in current scope (do not return them at least).
Why does the StreamWriter need to remain open if I've flushed its contents to the MemoryStream object already?
As @mikez mentioned, by default created StreamWriter
"owns" the underlying stream, but you can avoid this behaviour by adding leaveOpen = true
in constructor.
new StreamWriter(stream = s, encoding = Encoding.UTF8, bufferSize = 128, leaveOpen = true)
Upvotes: 2
Reputation: 8616
StreamWriter
automatically closes the Stream
if you don't tell it not to. Create it like this instead to leave the Stream
open:
using (var writer = new StreamWriter(stream, System.Text.Encoding.UTF8, 1024, true))
Alternatively, if you don't wish to pass the extra arguments, use GetBuffer
to access MemoryStream
's internal buffer. Avoid ToArray
as this creates a copy of the data and depending on your scenario may be inefficient.
Upvotes: 2
Reputation: 8240
The other answers indicate I should use the constructor that has a leaveOpen
param and set it to true, but I dislike this because the constructor also requires a bufferSize
argument.
However, I realized that I can get away with this just as easily:
// new method body, returns byte array
var stream = new MemoryStream();
using (var writer = new StreamWriter(stream))
{
writer.WriteLine(string.Join(",", columnNames));
foreach (var user in users)
{
var row = leaderboard.Metrics.Select(m => Faker.RandomNumber.Next().ToString()).ToList();
row.Insert(0, user.UserName);
writer.WriteLine(string.Join(",", row));
}
writer.Flush();
stream.Position = 0;
}
return stream.ToArray();
// consumer opens a new stream using the bytes
using (var stream = new MemoryStream(this.GetCSVStream(leaderboard, users)))
{
mockFile.Setup(f => f.InputStream).Returns(stream);
this.service.UpdateEntries(update.ID, mockFile.Object);
}
Upvotes: 0
Reputation: 40838
By default, the StreamWriter
"owns" the stream it is passed and will close it when disposed. Use the constructor that has a leaveOpen
boolean parameter. Set it to true to avoid closing the underlying Stream
when the writer is disposed in your first example.
Upvotes: 3