从代码引用中调用基本 class 方法

Calling the base class method from Code Quotations

假设我有两个简单的 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
        ()

我想做的是允许这样一种情况,即可以从外部传递 DerivedClass.PreStart 方法的实现主体。当然,我可以只传递一个可以在 PreStart 方法内部调用的函数,但这有一些与 base.PreStart() 方法的调用相关的缺点。我将不得不传递一个额外的参数来指示我想在覆盖主体之前或之后执行基本方法的任何内容。

我想做的是允许像这样定义正文:

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)

其中 baseFn() 是对基础 class 方法的调用。如果我们可以将委托传递给 base.PreStart 调用,这将很简单,但由于显而易见的原因,编译器不允许这样做。

我使用引号的简单实现如下:

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 按预期打印以下输出:

> d.PreStart();;
Base class PreStart method
PreStart overriden
Base class PreStart method
val it : unit = ()
> 

但是我对这段代码不满意

我的问题很简单。如何改进此代码以使其更符合 F# 的习惯?或者除了引用还有其他方法可以实现?

你真的不需要引号。显而易见的解决方案(行不通)是采用一个函数并将其作为参数传递给 base.PreStart(以便该函数可以决定何时调用基础 class 实现):

type DerivedClass1(preStart) =
    inherit BaseClass()
    override this.PreStart() =
        preStart base.PreStart

现在,这不起作用,因为 F# 只允许您直接调用 base.PreStart 并且不允许将其作为函数传递。但是,您可以定义一个调用基本 class 实现的私有 member,并将此新成员作为参数传递给 prestart:

type DerivedClass(preStart) =
    inherit BaseClass()
    member private this.BasePreStart() = 
        base.PreStart()
    override this.PreStart() =
        preStart this.BasePreStart