Reputation: 11439
I have a non-closing stream class that's wrapped in a using block with a binary reader, but for some reason when the block ends, my non-closing stream still closes.
The stream is defined as:
internal class NonClosingStream : Stream, IDisposable
{
private Stream baseStream;
public NonClosingStream(Stream baseStream)
{
this.baseStream = baseStream;
}
public override bool CanRead{ get { return baseStream.CanRead; } }
public override bool CanSeek{ get { return baseStream.CanSeek; } }
public override bool CanWrite { get { return baseStream.CanWrite; } }
public override void Flush()
{
baseStream.Flush();
}
public override long Length { get { return baseStream.Length; } }
public override long Position
{
get { return baseStream.Position; }
set { baseStream.Position = value; }
}
public override int Read(byte[] buffer, int offset, int count)
{
return baseStream.Read(buffer, offset, count);
}
public override long Seek(long offset, SeekOrigin origin)
{
return baseStream.Seek(offset, origin);
}
public override void SetLength(long value)
{
baseStream.SetLength(value);
}
public override void Write(byte[] buffer, int offset, int count)
{
baseStream.Write(buffer, offset, count);
}
public override void Close()
{
// Disconnects from base stream, but does not close it
this.baseStream = null;
}
void IDisposable.Dispose()
{
// Disconnects from base stream, but does not close it
this.baseStream = null;
}
}
and the read block looks like this:
public T Deserialize<T>(Stream stream)
{
using (NonClosingStream nonClosingStream = new NonClosingStream(stream))
using (BinaryReader reader = new BinaryReader(nonClosingStream, Encoding.ASCII, true))
{
// Read the type name, then convert it to an actual type
String typeName = reader.ReadString();
Type graphType = AvailableTypes.GetType(typeName);
// If a deserializer for this type already exists, use it.
if (deserializerFunctions.ContainsKey(graphType))
{
return (T)deserializerFunctions[graphType](reader);
}
// Otherwise, create one and use it
T graph = (T)FormatterServices.GetUninitializedObject(graphType);
typeof(ServiceSerializer).GetMethod("DeserializeObject",
BindingFlags.NonPublic | BindingFlags.Static)
.MakeGenericMethod(graphType)
.Invoke(this, new Object[] { reader, graph });
return graph;
}
}
what am I doing wrong?
Update
So I wrote this little diddy:
static void Main()
{
MemoryStream stream = new MemoryStream();
using (NonClosingStream nonCloser = new NonClosingStream(stream))
using (BinaryWriter writer = new BinaryWriter(nonCloser))
using (BinaryReader reader= new BinaryReader(nonCloser))
{
writer.Write("Lorem ipsum");
stream.Seek(0, SeekOrigin.Begin);
String data = reader.ReadString();
Console.WriteLine(data);
}
stream.Seek(0, SeekOrigin.Begin);
using (NonClosingStream nonCloser = new NonClosingStream(stream))
using (BinaryWriter writer = new BinaryWriter(nonCloser))
using (BinaryReader reader = new BinaryReader(nonCloser))
{
writer.Write("Lorem ipsum");
stream.Seek(0, SeekOrigin.Begin);
String data = reader.ReadString();
Console.WriteLine(data);
}
Console.ReadLine();
}
and it seems to work fine, the stream stays open like it's supposed to. So I guess the consensus is right. Somehow I'm closing the stream elsewhere. When I figure out what I'll post the results. Thanks all.
Update
Gaaaahhh, I figured out the problem. So the way the code works is that while it serializes/deserializes an object, it then builds a customized serializer out of expression trees and then compiles it so that future serializations are more fluid. That means my code is littered with stuff like this:
Action<BinaryReader, Object> assignmentAction = delegate(BinaryReader bReader, Object oGraph)
{
bReader.ReadByte(); // Read the next action
bReader.ReadString(); // Read the field name
bReader.ReadByte(); // Read the field type
// Call the assignment lambda
assignmentLambda(reader, deserializerFunctions[primitiveType], (T)oGraph);
};
did you catch that? No? Neither did I apparently. Let's add some context:
private static void DeserializeObject<T>(BinaryReader reader, T graph)
{
...
Action<BinaryReader, Object> assignmentAction = delegate(BinaryReader bReader, Object oGraph)
{
bReader.ReadByte(); // Read the next action
bReader.ReadString(); // Read the field name
bReader.ReadByte(); // Read the field type
// Call the assignment lambda
assignmentLambda(reader, deserializerFunctions[primitiveType], (T)oGraph);
};
...
}
The lambda is closing over reader
from the outside block, instead of using the bReader
provided when the cached deserializer runs. Hence, when the deserializer runs, it's using an already discarded Binary reader object rather than the fresh one being provided to it. I guess the issue wasn't that I was closing the stream, but that I was using a disposed reader. At least that explains why it would work once, and then fail the second time, since the second time it relies on the cached deserializer. Oops!
Thanks all.
Upvotes: 3
Views: 566
Reputation: 941277
void IDisposable.Dispose()
Your class has two Dispose() methods. The one you explicitly implemented. And the one that you inherited from the Stream class. Problem is, BinaryStream doesn't know beans about yours. It only knows about the one that Stream implements. Furthermore, when you use the BinaryStream(Stream) constructor, the BinaryStream object assumes ownership of the passed Stream object. Which means it will dispose that stream when it gets disposed itself. Perhaps you see the problem now, the inherited Dispose() method will be called, not yours. And it closes the base stream.
This is why Stream implements the Dispose pattern. You'll need to make it look like this instead:
internal class NonClosingStream : Stream {
protected override Dispose(bool disposing) {}
}
Upvotes: 1
Reputation: 100527
Since your stream does not create inner stream most likely outer code closes your inner stream. Chances are your code look like:
NonClosingStream nonClosing;
using(var stream = new FileStream(...))
{
nonClosing = new NonClosingStream(stream );
....
}
// inner stream now closed and nonClosing will fail all operations.
Upvotes: 1
Reputation: 5689
It depends on whether the stream which is being wrapped by your NonClosingStream class is referenced elsewhere. If not, then the underlying stream will have no references, so at some point afterwards its finalizer will close the stream.
Upvotes: 0