给定一个函数列表,如何提取包含列表中每个函数的第一个参数类型的列表?
列表定义为:
let messageHandlers = [
fun (message: MessageA) -> (), // Cast
fun (message: MessageB) -> () // Cast
]
类型列表可以定义为:
let getFirstParam x = x.GetType().UseReflectionToGetTheParameters
let types = List.map getFirstParam messageHandlers
我希望Parameters
或FSharpFunc
上类似的列表,但是我找不到。
如何以静态方式获取类型,以避免错误的风险,像这样:
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.GetFunctionElements
和FSharpType.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