将 'obj' 动态转换为反射指示的函数类型

Dynamically cast 'obj' to function type indicated by reflection

我正在对 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,但是 validatorvalue 都是 obj 类型并且它们的类型只在运行时才知道,所以我该怎么做?

请注意,genericTypeInstantiation 是一个 System.Type,它包含 validator 参数的类型。

你应该可以用一点反射魔法来做到这一点

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 "..."