我模式匹配F#引用,并希望调用引号内引用的函数。简化代码如下所示:
open Microsoft.FSharp.Quotations.Patterns
open Microsoft.FSharp.Quotations.DerivedPatterns
let ModelValidator (validator : 'T -> bool) (valueToValidate : 'T) : 'T =
failwith "Don't call me!"
let foo expr =
match expr with
| SpecificCall <@ ModelValidator @> (None, genericTypeInstantiation::[],
[ Value(validator, validatorType); source ]) ->
let value = getValue source // Will return an 'obj'
// Now, I want to call 'validator value', but how?
// This will obviously not work, but it shows in principle what I want to do:
let castedValue : genericTypeInstantiation = unbox value
let castedFunction : genericTypeInstantiation -> bool = unbox validator
castedFunction castedValue
| _ -> failwith "..."
let myIntValidator (x : int) =
x = 42
let myStringValidator (x : string) =
x = "foo"
let a = foo <@ ModelValidator myIntValidator 44 @>
let b = foo <@ ModelValidator myStringValidator "bar" @>
虚函数ModelValidator
是通用的,用作一种占位符或标记,即在模式匹配中放入匹配大小写的东西。它还有助于在报价中强制实施类型安全。
在调用ModelValidator
的匹配案例中,我想致电validator value
,但validator
和value
都属于obj
类型类型只在运行时知道,所以我该怎么做?
请注意,genericTypeInstantiation
是System.Type
,其中包含validator
参数的类型。
答案 0 :(得分:3)
你应该可以用一点反射魔法来做到这一点
open Microsoft.FSharp.Quotations.Patterns
open Microsoft.FSharp.Quotations.DerivedPatterns
let ModelValidator (validator : 'T -> bool) (valueToValidate : 'T) : 'T =
failwith "Don't call me!"
type Invoker private () =
static let CallMethodInfo =
let flags = System.Reflection.BindingFlags.NonPublic ||| System.Reflection.BindingFlags.Static
typeof<Invoker>.GetMethod("DoCall", flags).GetGenericMethodDefinition()
static member private DoCall<'T>(validator: obj, value: obj): bool =
let validator: 'T -> bool = unbox validator
let value: 'T = unbox value
validator value
static member Call(validator: obj, value: obj, typeOfValue: System.Type): bool =
CallMethodInfo.MakeGenericMethod(typeOfValue).Invoke(null, [|validator; value|]) :?> _
let foo expr =
match expr with
| SpecificCall <@ ModelValidator @> (None, [genericTypeInstantiation], [ Value(validator, validatorType); source ]) ->
let value = getValue source // Will return an 'obj'
Invoker.Call(validator, value, genericTypeInstantiation)
| _ -> failwith "..."