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

时间:2016-11-10 01:57:49

标签: reflection f#

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

该列表定义为:

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

然后可以将类型列表定义为:

let getFirstParam x = x.GetType().UseReflectionToGetTheParameters

let types = List.map getFirstParam messageHandlers

我原本期望在Parameters上有一个名为FSharpFunc或类似内容的列表,但我找不到。

3 个答案:

答案 0 :(得分:4)

如何静态获取类型,以避免出现错误的风险,如下所示:

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

答案 1 :(得分:3)

首先,列表不能包含不同类型的元素。因此,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<_,_>。来自FSharpType.GetFunctionElements派生

更新:正如 kvb 在评论中指出的,对于更可靠的解决方案,可以使用FSharpType.IsFunctionlet getFunctionParamType fn = if FSharpType.IsFunction fn && let input, output = FSharpType.GetFunctionElements fn Some input else None 个功能,基本上以一种更方便,F#友好的方式包装上述逻辑:

group by

小心虽然:反思是一件棘手的事情,很容易出错并且容易出现无声的失败。从你的问题来看,你并不能真正理解它是如何运作的,这对于使用它是一个强有力的禁忌。也许如果你描述了你的首要问题,那么有人就能提供更好的解决方案。

答案 2 :(得分:2)

受到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