I want to derive a class from System.IO.BinaryWriter, and call it's constructor with a custom Stream, an implementation of which captures a constructor argument of the derived type. For the life of me, I cannot figure out whether even this is possible at all. What I am essentially trying to do, slightly trimmed to be an MCV, is
type HashingBinaryWriter private
(hasher : System.Security.Cryptography.HashAlgorithm,
stream : System.IO.Stream) =
inherit System.IO.BinaryWriter(stream)
let unsupp() = raise(System.NotSupportedException())
let hash_stream =
{ new System.IO.Stream() with
member __.CanRead = false
member __.CanSeek = false
member __.CanWrite = true
member __.Length = unsupp()
member __.Position with get() = unsupp() and set(_) = unsupp()
member __.Seek(_,_) = unsupp()
member __.SetLength _ = unsupp()
member __.Read(_,_,_) = unsupp()
member __.Flush() = ()
member __.Write(buffer, offset, count) =
hasher.TransformBlock(buffer, offset, count, null, 0) |> ignore
}
new(hasher) = new HashingBinaryWriter(hasher, hash_stream)
// Or, alternatively
new(hasher) as me = new HashingBinaryWriter(hasher, me.hash_stream)
The last line fails to compile because hash_stream is undefined, in either form. Apparently, as this answer suggests, the scope of constructor arguments is different from that of class declaration body, but I need to understand what is going on here (and if possible the why behind the F# design decision).
Indeed, I can see some workarounds (convert hash_stream to a private property, for example), but my vocabulary of F# idioms is lacking this one. My second question is, then, what would be the idiomatic way of doing this.
There are multiple ways to do this - and I guess the right choice depends on how your complete implementation will look.
If you wanted something that is as close to the version in your question as possible, then you can just move unsupp and hash_stream inside the constructor:
type HashingBinaryWriter private
(hasher : System.Security.Cryptography.HashAlgorithm,
stream : System.IO.Stream) =
inherit System.IO.BinaryWriter(stream)
new(hasher : System.Security.Cryptography.HashAlgorithm) =
let unsupp() = raise(System.NotSupportedException())
let hash_stream =
{ new System.IO.Stream() with
member __.CanRead = false
member __.CanSeek = false
member __.CanWrite = true
member __.Length = unsupp()
member __.Position with get() = unsupp() and set(_) = unsupp()
member __.Seek(_,_) = unsupp()
member __.SetLength _ = unsupp()
member __.Read(_,_,_) = unsupp()
member __.Flush() = ()
member __.Write(buffer, offset, count) =
hasher.TransformBlock(buffer, offset, count, null, 0) |> ignore
}
new HashingBinaryWriter(hasher, hash_stream)
I guess that this can easily get ugly if your implementation of hash_stream gets longer. In that case, it would make more sense to do what John suggests in the comments and move the implementation of the stream outside of the class, perhaps into a helper module:
let unsupp() = raise(System.NotSupportedException())
let createHashStream (hasher : System.Security.Cryptography.HashAlgorithm) =
{ new System.IO.Stream() with
member __.CanRead = false
member __.CanSeek = false
member __.CanWrite = true
member __.Length = unsupp()
member __.Position with get() = unsupp() and set(_) = unsupp()
member __.Seek(_,_) = unsupp()
member __.SetLength _ = unsupp()
member __.Read(_,_,_) = unsupp()
member __.Flush() = ()
member __.Write(buffer, offset, count) =
hasher.TransformBlock(buffer, offset, count, null, 0) |> ignore
}
type HashingBinaryWriter private
(hasher : System.Security.Cryptography.HashAlgorithm,
stream : System.IO.Stream) =
inherit System.IO.BinaryWriter(stream)
new(hasher : System.Security.Cryptography.HashAlgorithm) =
new HashingBinaryWriter(hasher, createHashStream hasher)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With