在f#中如何获得函数的参数?



给定一个函数列表,如何提取包含列表中每个函数的第一个参数类型的列表?

列表定义为:

let messageHandlers = [
    fun (message: MessageA) -> (), // Cast
    fun (message: MessageB) -> () // Cast
]

类型列表可以定义为:

let getFirstParam x = x.GetType().UseReflectionToGetTheParameters
let types = List.map getFirstParam messageHandlers

我希望ParametersFSharpFunc上类似的列表,但是我找不到。

如何以静态方式获取类型,以避免错误的风险,像这样:

let messageHandlers, types =
    let withArgType (f: 'T -> unit) = (f :> obj, typeof<'T>)
    [
        withArgType (fun (param1: MessageA) -> ())
        withArgType (fun (param1: MessageB) -> ())
    ]
    |> List.unzip

首先,一个列表不能包含不同类型的元素。因此,methods列表中的所有函数将具有完全相同的第一个参数。

但是,好吧,您可以通过删除函数的类型(即将它们转换为obj)在技术上绕过这个问题:

let methods = [
   (fun (param1: MyRecordType) -> ()) :> obj
   (fun (param1: AnotherType) -> ()) :> obj
]

现在您已经得到了一个漂亮的obj list,其中每个元素实际上都是一个函数。除了在编译时不知道,因为您已经将它们强制转换为obj

现在,f#中的函数由类FSharpFunc<_,_>表示。第一个通用参数是输入,第二个是输出。所以你可以取第一个泛型参数,这就是你的答案:

let paramType = fn.GetType().GetGenericArguments().[0]

除了我也会把一个保护措施到位,以确保我传递的obj实际上是一个函数:

let funcType = typeof<FSharpFunc<_,_>>.GetGenericTypeDefinition()
let getFunctionParamType fn =
   let fnType = fn.GetType()
   if fnType.IsGenericType && 
      funcType.IsAssignableFrom (fnType.GetGenericTypeDefinition())
   then
      Some (fnType.GetGenericArguments().[0])
   else
      None

注意:有必要使用funcType.IsAssignableFrom(而不是仅仅与funcType =比较),因为一些函数可以作为自定义类来实现,该类派生自 FSharpFunc<_,_>

更新:正如kvb在评论中指出的那样,为了更可靠的解决方案,可以使用FSharpType.GetFunctionElementsFSharpType.IsFunction函数,它们实质上以更方便,f#友好的方式包装了上述逻辑:

let getFunctionParamType fn =
   if FSharpType.IsFunction fn && 
      let input, output = FSharpType.GetFunctionElements fn
      Some input
   else
      None

小心:反射是一个棘手的事情,容易出错,容易出现无声的失败。从你的问题来看,你并没有真正理解它是如何工作的,这将是一个强烈的禁忌症。如果你能描述一下你的首要问题,也许有人能提供一个更好的解决方案。

受Tarmil的启发,我最终得到了静态的类型,但我也将每个函数包装在一个更通用的函数中。

let messageHandlers, types =
    let withArgType (f: 'T -> unit) = 
        let genericFunc = fun (o: obj) -> (f (o :?> 'T))
        (genericFunc, typeof<'T>)
    [
        withArgType (fun (message: MessageA) -> ())
        withArgType (fun (message: MessageB) -> ())
    ]
    |> List.unzip

相关内容

  • 没有找到相关文章

最新更新