简体   繁体   English

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

[英]In F# how do you get the parameters for a function?

Given a list of functions, how do you extract a list containing the type of the first parameter for each function in the list? 给定一个函数列表,如何提取包含列表中每个函数的第一个参数类型的列表?

The list is defined as: 该列表定义为:

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

The list of types could then be defined as: 然后可以将类型列表定义为:

let getFirstParam x = x.GetType().UseReflectionToGetTheParameters

let types = List.map getFirstParam messageHandlers

I would have expected a list called Parameters or something similar on the FSharpFunc however I can't find one. FSharpFunc期望在FSharpFunc上有一个名为Parameters或类似的FSharpFunc但是我找不到一个。

How about getting the types statically instead, to avoid the risk of error, like this: 如何静态获取类型,以避免出错的风险,如下所示:

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

Firstly, a list can't contain elements of different types. 首先,列表不能包含不同类型的元素。 Therefore, all the functions in your methods list will have the exact same first parameter. 因此, methods列表中的所有函数都将具有完全相同的第一个参数。

But ok, you can technically get around that by erasing the type of the functions (ie casting them to obj ): 但是好的,你可以通过擦除函数的类型(即将它们转换为obj )在技术上解决这个问题:

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

Now you've got yourself a nice obj list , in which every element is actually a function. 现在你已经有了一个很好的obj list ,其中每个元素实际上都是一个函数。 Except that's not known at compile time, because you've casted them to obj . 除了在编译时不知道,因为你已经将它们转换为obj

Now, functions in F# are represented by the class FSharpFunc<_,_> . 现在,F#中的函数由类FSharpFunc<_,_> The first generic parameter is input, the second - output. 第一个通用参数是输入,第二个是输出。 So you can just take the first generic argument, and that's your answer: 所以你可以采取第一个通用参数,这就是你的答案:

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

Except I would also put a safeguard in place to make sure that the obj I'm passed is actually a function: 除了我还要设置一个安全措施,以确保我通过的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

NOTE: it is necessary to use funcType.IsAssignableFrom (as opposed to just comparing with funcType = ), because some functions could be implemented as a custom class that's derived from FSharpFunc<_,_> . 注意:必须使用funcType.IsAssignableFrom (而不是仅仅与funcType =进行比较),因为某些函数可以实现为从FSharpFunc<_,_> 派生的自定义类。

Update : as kvb points out in the comments, for a more solid solution, one may use FSharpType.GetFunctionElements and FSharpType.IsFunction functions, which essentially wrap the above logic in a more convenient, F#-friendly way: 更新 :正如kvb在评论中指出的那样,对于更可靠的解决方案,可以使用FSharpType.GetFunctionElementsFSharpType.IsFunction函数,它们基本上以更方便,F#友好的方式包装上述逻辑:

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

Beware though: reflection is a tricky thing, easy to get wrong and prone to silent failures. 请注意 :反射是一件棘手的事情,很容易出错并且容易出现无声的失败。 Judging by your question, you don't really understand how it all works, which would be a strong contraindication for using it. 从你的问题来看,你并不真正理解这一切是如何运作的,这将成为使用它的强烈禁忌症。 Perhaps if you described your overarching problem, someone would be able to offer a better solution. 也许如果你描述了你的首要问题,那么有人就能提供更好的解决方案。

Inspired by Tarmil's great answer I ended up getting the types statically but I also wrapped each function in a more general function. 受到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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM