给定一个函数列表,如何提取包含列表中每个函数的第一个参数类型的列表?
该列表定义为:
let messageHandlers = [
fun (message: MessageA) -> (), // Cast
fun (message: MessageB) -> () // Cast
]
然后可以将类型列表定义为:
let getFirstParam x = x.GetType().UseReflectionToGetTheParameters
let types = List.map getFirstParam messageHandlers
我原本期望在Parameters
上有一个名为FSharpFunc
或类似内容的列表,但我找不到。
答案 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.IsFunction
和let 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