Inheritance problem with BinaryWriter

E

Eugene

I'm trying to write a class which uses BinaryWriter as its base but allows
for queuing of write requests
Public Class QueuedBinaryWriter
Inherits BinaryWriter

I override all the Write methods like so:
Public Overloads Overrides Sub Write(ByVal Value As Byte)
m_Queue.Enqueue(New WriteRequest(MyBase.BaseStream.Position, Value))
End Sub
'same for all other Write variants

I also add an overloaded Write method for my own datatype
private Overloads Sub Write(ByVal Request As WriteRequest)
Dim t As Type = Request.Type

If t Is GetType(Boolean) Then
MyBase.Write(DirectCast(Request.Value, Boolean))
ElseIf t Is GetType(Byte) Then
MyBase.Write(DirectCast(Request.Value, Byte))
' etc...
end sub

and finally I override the Flush() method
Public Overrides Sub Flush()
For Each Request As WriteRequest In m_Queue
Seek(CInt(Request.Offset), SeekOrigin.Begin)
Write(Request)
Next
MyBase.Flush()
m_Queue.Clear()
End Sub

The problem is in the MyBase.Write() call in the Write(WriteRequest) method.
It doesn't call the base class Write but instead calls
QueuedBinaryWriter.Write(). This leads to an infinite loop since each
QueuedBinaryWriter.Write() enqueues another WriteRequest.

I checked the IL:
IL_0009: ldtoken [mscorlib]System.Boolean
IL_000e: call class [mscorlib]System.Type
[mscorlib]System.Type::GetTypeFromHandle(valuetype
[mscorlib]System.RuntimeTypeHandle)
IL_0013: bne.un.s IL_0031
IL_0015: ldarg.0
IL_0016: ldarg.1
IL_0017: ldfld object
QueuedBinaryWriter.ciloci.QueuedBinaryWriter/WriteRequest::Value
IL_001c: unbox [mscorlib]System.Boolean
IL_0021: ldobj [mscorlib]System.Boolean
IL_0026: call instance void
[mscorlib]System.IO.BinaryWriter::Write(bool)

Any suggestions on how to solve this problem?

Thank you for your time.
 
J

Jay B. Harlow [MVP - Outlook]

Eugene,
First question: What are you really attempting to do??

The Binary Writer (underlying stream really) is able to buffer the write
requests for you, why are you putting the values in a Queue? Especially a
queue that holds the position to write to! If you call write with 5 bytes &
then call Flush, I would expect all 5 bytes are going to be at position 0 as
you have not "actually" written anything yet, which totally doesn't make
sense!!


Second question, can you post your complete source & sample code that fully
demonstrates the problem, I am not able to recreate the problem.


Third: You do realize That calling MyBase.BaseStream forces the writer to be
flushed, so as to ensure the Position & other properties of the Stream
returned is valid (avoiding my comment from my first question). This
indirect flushing of the basestream causes your writer to be flushed,
causing QueuedBinaryWriter.Write to be called... Which causes your "infinite
loop"

Any suggestions on how to solve this problem?
Does your QueuedBinaryWriter have to inherit from BinaryWriter? Can it
contain a BinaryWriter instead?
Public Class QueuedBinaryWriter

Private Readonly m_writer As BinaryWriter
Private Readonly m_queue As Queue

Public Sub Write(value As Byte)
m_Queue.Enqueue(New WriteRequest(MyBase.BaseStream.Position,
Value))
End Sub

Public Sub Write(value As Char)
m_Queue.Enqueue(New WriteRequest(MyBase.BaseStream.Position,
Value))
End Sub

Alternatively write Enqueue WriteRequest so it does not rely on
MyBase.BaseStream.

Hope this helps
Jay

Eugene said:
I'm trying to write a class which uses BinaryWriter as its base but allows
for queuing of write requests
Public Class QueuedBinaryWriter
Inherits BinaryWriter

I override all the Write methods like so:
Public Overloads Overrides Sub Write(ByVal Value As Byte)
m_Queue.Enqueue(New WriteRequest(MyBase.BaseStream.Position, Value))
End Sub
'same for all other Write variants

I also add an overloaded Write method for my own datatype
private Overloads Sub Write(ByVal Request As WriteRequest)
Dim t As Type = Request.Type

If t Is GetType(Boolean) Then
MyBase.Write(DirectCast(Request.Value, Boolean))
ElseIf t Is GetType(Byte) Then
MyBase.Write(DirectCast(Request.Value, Byte))
' etc...
end sub

and finally I override the Flush() method
Public Overrides Sub Flush()
For Each Request As WriteRequest In m_Queue
Seek(CInt(Request.Offset), SeekOrigin.Begin)
Write(Request)
Next
MyBase.Flush()
m_Queue.Clear()
End Sub

The problem is in the MyBase.Write() call in the Write(WriteRequest) method.
It doesn't call the base class Write but instead calls
QueuedBinaryWriter.Write(). This leads to an infinite loop since each
QueuedBinaryWriter.Write() enqueues another WriteRequest.

I checked the IL:
IL_0009: ldtoken [mscorlib]System.Boolean
IL_000e: call class [mscorlib]System.Type
[mscorlib]System.Type::GetTypeFromHandle(valuetype
[mscorlib]System.RuntimeTypeHandle)
IL_0013: bne.un.s IL_0031
IL_0015: ldarg.0
IL_0016: ldarg.1
IL_0017: ldfld object
QueuedBinaryWriter.ciloci.QueuedBinaryWriter/WriteRequest::Value
IL_001c: unbox [mscorlib]System.Boolean
IL_0021: ldobj [mscorlib]System.Boolean
IL_0026: call instance void
[mscorlib]System.IO.BinaryWriter::Write(bool)

Any suggestions on how to solve this problem?

Thank you for your time.
 
E

Eugene

Hi Jay,

Thanks for your response.
First question: What are you really attempting to do??
I'm trying to create a Hex Editor type application. The idea behind the
queueing was to allow the user to undo any changes to the file even after
saving. Thinking back, Queue is not really a good description. It's a
queue when writing but it's a stack when undoing.

I've thought about the design more and I've dumped the QueuedBinaryWriter
and am working on another approach.
The Binary Writer (underlying stream really) is able to buffer the write
requests for you, why are you putting the values in a Queue? Especially a
queue that holds the position to write to! If you call write with 5 bytes
& then call Flush, I would expect all 5 bytes are going to be at position
0 as you have not "actually" written anything yet, which totally doesn't
make sense!!
Yeah, I caught that bug during unit testing. Forgot to seek after a write
Second question, can you post your complete source & sample code that
fully demonstrates the problem, I am not able to recreate the problem.
Just my luck. Yesterday I was able to write a 30 line program that
demonstrated the problem. Today, I can't recreate it. I thought I'd
tracked down the cause of the "problem" yesterday after I posted.
Replacing
public Overloads Overrides Sub Write(arg)
with
public Overloads Sub Write(arg)
seemed to solve the problem at that time. If nothing I learned that
"Overloads" implies "Overrides" in method declarations in Visual Basic.
Third: You do realize That calling MyBase.BaseStream forces the writer to
be flushed, so as to ensure the Position & other properties of the Stream
returned is valid (avoiding my comment from my first question). This
indirect flushing of the basestream causes your writer to be flushed,
causing QueuedBinaryWriter.Write to be called... Which causes your
"infinite loop"
I know now!
Does your QueuedBinaryWriter have to inherit from BinaryWriter? Can it
contain a BinaryWriter instead?
I was divided between classical inheritance versus containment/delegation.
I figured since a QueuedBinaryWriter _is_ a BinaryWriter but with extra
features I should inherit.
Alternatively write Enqueue WriteRequest so it does not rely on
MyBase.BaseStream.
Is there a better way to get the current position in the stream? The only
thing I can think of is keeping the position in a member variable.

Thanks.
 
J

Jay B. Harlow [MVP - Outlook]

Eugene,
I'm trying to create a Hex Editor type application. The idea behind the
queueing was to allow the user to undo any changes to the file even after
The "easiest" way I have found to implement Undo is to implement the Command
Pattern.

The GOF book "Design Patterns - Elements of Reusable Object-Oriented
Software" from Addison Wesley, contains a description of the Command
Pattern, plus how to implement it such that it supports Undo & Redo. GOF =
Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides.
Is there a better way to get the current position in the stream? The only
thing I can think of is keeping the position in a member variable.
If I needed to actually implement a QueuedBinaryWriter type class and I
needed an unaltered copy of the Stream, I would keep my own copy of the
Stream, my QueuedBinaryWriter would have a single Constructor that expected
a Stream, I would pass the stream to the base class's constructor as well as
keeping my own copy. However! your problem does not really sound like it
should be inheriting from BinaryWriter.

Hope this helps
Jay
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top