Let's say I have two simple classes :
type BaseClass() =
abstract member PreStart : unit -> unit
default x.PreStart() =
printfn "Base class PreStart method"
type DerivedClass() as this =
inherit BaseClass()
override this.PreStart() =
// I want to pass the body from outside
()
What I'm trying to do is to allow the scenario when one could pass an implementation body of the DerivedClass.PreStart
method from outside. Of course I could pass just a function that could be called inside the PreStart
method but this has some drawback related to the call of base.PreStart()
method. I would have to pass an extra parameter to indicate whatever I want to execute the base method before the overriden body or after if at all.
What I would like to do is to allow the definition of the body like that :
let preStart = (fun (baseFn : unit->unit) ->
baseFn() // base class call before the overriden body
printfn "PreStart overriden"
baseFn() // base class call after the overriden body)
where baseFn()
is a call to the base class method. This would be simple if we could pass around the delegate to base.PreStart
call but this is not allowed by the compiler for obvious reasons.
My naive implementation with Quotations is as follows :
open FSharp.Quotations
open Microsoft.FSharp.Linq
open Microsoft.FSharp.Linq.QuotationEvaluation
open System.Linq.Expressions
type BaseClass() =
abstract member PreStart : unit -> unit
default x.PreStart() =
printfn "Base class PreStart method"
type DerivedClass(expression : Expr<(unit -> obj) -> obj>) as this =
inherit BaseClass()
// this is not optimal <@@ this :> BaseClass @@> doesn't work
let baseClinst = new BaseClass()
let bexpr = <@@ baseClinst @@>
let overrideExpr = expression
let baseTy = typeof<BaseClass>
let mthd = baseTy.GetMethod("PreStart")
override this.PreStart() =
let baseMthd = Expr.Call(bexpr, mthd, [])
let combined = <@ ((%overrideExpr) (baseMthd.CompileUntyped())) @>
let l = QuotationEvaluator.EvaluateUntyped combined
()
let preStart = <@
fun (baseFn : unit->obj) ->
baseFn() // base class call before the overriden body
printfn "PreStart overriden"
baseFn() // base class call after the overriden body
@>
let d = new DerivedClass(preStart)
if prints the following output as expected:
> d.PreStart();;
Base class PreStart method
PreStart overriden
Base class PreStart method
val it : unit = ()
>
However I'm not happy with this code
- In the
DerivedClass
I have to create an instance of the base classlet baseClinst = new BaseClass()
which servs as a base expression parameter toExpr.Call
. Doing this doesn't worklet bexpr = <@@ this :> BaseClass @@>
. This is just calling another's instance of base class retrieved by reflection. - I had to modify the type of the function from
unit -> unit
tounit -> obj
because of the return of the call tobaseMthd.CompileUntyped()
- It certainly not optimal and ugly.
My question is simple. How to improve this code to be more idiomatic F#? Or maybe there are other means than Quotations to achieve it?