如何通过反射调用返回类型未知的方法并完成函数组合?
问题说明
先看几个函数示例:
let bar : 'a -> int = fun _ -> 1
let foo : unit -> string = fun () -> "its fine"
它们的组合逻辑(注意泛型参数'a会被自动推断,具体类型不影响最终结果):
let fooBar : unit -> int = fun () -> bar (foo ())
现在需要通过反射调用foo方法,但无法提前知晓它的返回类型,要把调用逻辑封装成unit -> int的函数,当前代码无法编译:
let composeReflectedMethod : string -> (unit -> int) = let dll = Assembly.LoadFile(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "foo.dll")) let ``type`` = dll.GetType("Foo") let fooMethod = ``type``.GetMethod("foo") // 不知道foo的返回类型,但逻辑上不需要关心 // 无论返回什么类型都能传入bar处理 // 但Invoke默认返回obj类型 let ifooWhat = fooMethod.Invoke( null, [| () |]) // 此处无法编译:bar需要泛型参数'a,但ifooWhat是obj类型 fun () -> bar ifooWhat
注:实际代码复杂度更高,忽略示例表面的无意义性。
补充说明:示例中bar看似不使用参数类型,但实际场景中会用到返回类型的特性,只是难以用简单示例准确演示,不能直接用obj作为类型应付所有情况。
可行解决方案
核心问题是反射返回的obj类型无法直接匹配bar的泛型参数'a,以下是两种解决思路:
方案1:显式指定泛型参数
因为bar的泛型参数可以接受任意类型,包括obj,所以直接显式指定bar的泛型参数为obj即可:
let composeReflectedMethod : string -> (unit -> int) = let dll = Assembly.LoadFile(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "foo.dll")) let ``type`` = dll.GetType("Foo") let fooMethod = ``type``.GetMethod("foo") fun () -> let ifooWhat = fooMethod.Invoke(null, [| () |]) // 显式指定bar的泛型参数为obj,让编译器通过类型检查 bar<obj> ifooWhat
这种方式最简单,只要bar的泛型实现兼容obj类型就可行。如果实际场景中bar需要参数的具体类型特性,可以先通过反射获取ifooWhat的实际类型,再做类型转换或动态调用:
// 适配需要具体类型的场景 let composeReflectedMethod : string -> (unit -> int) = let dll = Assembly.LoadFile(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "foo.dll")) let ``type`` = dll.GetType("Foo") let fooMethod = ``type``.GetMethod("foo") // 获取bar的泛型方法定义(假设bar在名为YourModule的模块中) let barGenericDef = typeof<YourModule>.GetMethod("bar", BindingFlags.Public ||| BindingFlags.Static) fun () -> let ifooWhat = fooMethod.Invoke(null, [| () |]) let actualReturnType = ifooWhat.GetType() // 构造对应实际类型的泛型bar方法并调用 let barForType = barGenericDef.MakeGenericMethod(actualReturnType) barForType.Invoke(null, [| ifooWhat |]) :?> int
方案2:利用F#类型推断特性
如果不想显式指定泛型参数,可以通过局部函数让编译器自动推断类型:
let composeReflectedMethod : string -> (unit -> int) = let dll = Assembly.LoadFile(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "foo.dll")) let ``type`` = dll.GetType("Foo") let fooMethod = ``type``.GetMethod("foo") // 局部函数封装bar调用,让编译器推断参数类型 let wrapBar (input: obj) = bar input fun () -> let ifooWhat = fooMethod.Invoke(null, [| () |]) wrapBar ifooWhat
这里编译器会自动推断wrapBar的参数类型为obj,进而匹配bar的泛型参数'a,通过类型检查。
内容的提问来源于stack exchange,提问作者MrD at KookerellaLtd
相关产品推荐
相关产品推荐

