【问题标题】:Dynamic Function Call in FSharpFSharp 中的动态函数调用
【发布时间】:2017-08-13 03:30:51
【问题描述】:

是否可以采用 FSharp 函数并将其转换为 Dynamic 函数,或者将来 FSharp 是否会出现类似的功能?

let func (a:int) (b:int) : int =
    a + b

let dynamicFunc = FSharpFunc.ToDynamicFunc(func)

let argumentList = [1; 2]

let object = dynamicFunc argumentList 

let result = object :?> int

看来您目前必须回退到标准反射(例如:calling a F# function by name)但是,这种方法似乎非常脆弱。主要是因为没有真正的保证它会起作用,而且你必须知道幕后发生了什么。

这样的东西可以用来包装任何函数并动态地做事。

let wrapFun (x:'f) : 'f =

    let args = FSharp.Reflection.FSharpType.GetFunctionElements <| x.GetType()

    let runner (any:obj list) : obj =
        // Do extra things
        FSharpFunc.DynamicInvoke x

    FSharp.Reflection.FSharpValue.MakeFunction (typeof<'f>, runner) :?> 'f

【问题讨论】:

  • 你能给出一个更真实的用例吗?有多种方法可以动态地做事,但如果可能的话,我通常会避免使用它们,因为它们只会更容易引入错误。在您的特定示例中,您有一个具有静态已知类型的函数 func,因此动态调用它似乎没有帮助。
  • 在这种情况下,我在考虑远程调用,因此调用包装函数会收集参数并将它们发送到远程机器,在那里它会实际调用该函数。

标签: function dynamic f#


【解决方案1】:

F# 确实支持动态调用运算符。但是你必须实现你的。这是取自http://www.fssnip.net/2U/title/Dynamic-operator-using-Dynamic-Language-Runtime的示例实现

// Reference C# implementation of dynamic operations
#r "Microsoft.CSharp.dll"
open System
open System.Runtime.CompilerServices
open Microsoft.CSharp.RuntimeBinder

// Simple implementation of ? operator that works for instance
// method calls that take a single argument and return some result
let (?) (inst:obj) name (arg:'T) : 'R =
  // TODO: For efficient implementation, consider caching of call sites 
  // Create dynamic call site for converting result to type 'R
  let convertSite = 
    CallSite<Func<CallSite, Object, 'R>>.Create
      (Binder.Convert(CSharpBinderFlags.None, typeof<'R>, null))

  // Create call site for performing call to method with the given
  // name and a single parameter of type 'T
  let callSite = 
    CallSite<Func<CallSite, Object, 'T, Object>>.Create
      (Binder.InvokeMember
        ( CSharpBinderFlags.None, name, null, null, 
          [| CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null);
             CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) |]))

  // Run the method call using second call site and then 
  // convert the result to the specified type using first call site
  convertSite.Target.Invoke
    (convertSite, callSite.Target.Invoke(callSite, inst, arg))

你可以按如下方式使用它

// Dynamically invoke 'Next' method of 'Random' type
let o = box (new Random())
let a : int = o?Next(10)

至于参数,您必须将它们作为 Tuple 之类的东西传递

target?method(param1, param2) 这意味着目标方法将其参数作为元组处理,因此可能会或可能不会涉及某些模式匹配

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-21
    • 2021-07-06
    • 2018-07-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多