从代码引用中调用基本 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 = ()
>
但是我对这段代码不满意
- 在
DerivedClass
中,我必须创建基础 class let baseClinst = new BaseClass()
的实例,作为 Expr.Call
的基础表达式参数。这样做是行不通的 let bexpr = <@@ this :> BaseClass @@>
。这只是调用另一个通过反射检索到的 base class 实例。
- 由于调用
baseMthd.CompileUntyped()
的 return,我不得不将函数类型从 unit -> unit
修改为 unit -> obj
- 它肯定不是最优的和丑陋的。
我的问题很简单。如何改进此代码以使其更符合 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
假设我有两个简单的 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 = ()
>
但是我对这段代码不满意
- 在
DerivedClass
中,我必须创建基础 classlet baseClinst = new BaseClass()
的实例,作为Expr.Call
的基础表达式参数。这样做是行不通的let bexpr = <@@ this :> BaseClass @@>
。这只是调用另一个通过反射检索到的 base class 实例。 - 由于调用
baseMthd.CompileUntyped()
的 return,我不得不将函数类型从 - 它肯定不是最优的和丑陋的。
unit -> unit
修改为 unit -> obj
我的问题很简单。如何改进此代码以使其更符合 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