我想编写一个函数,将函数 f 作为参数并返回与 f 关联的 System.Reflection.MethodInfo.
I would like to write a function that takes a function f as an argument and returns the System.Reflection.MethodInfo associated to f.
我不太确定这是否可行.
I'm not quite sure if it is feasible or not.
推荐答案所以,我终于找到了解决方案.非常hacky,但是嘿!有用!(仅在调试模式下).
So, I finally found a solution. Very hacky, but hey! It works! (edit: in Debug mode only).
let Foo (f:S -> A[] -> B[] -> C[] -> D[] -> unit) = let ty = f.GetType() let argty = [|typeof<S>; typeof<A[]>; typeof<B[]>; typeof<C[]>;typeof<D[]>|] let mi = ty.GetMethod("Invoke", argty) let il = mi.GetMethodBody().GetILAsByteArray() let offset = 9//mi.GetMethodBody().MaxStackSize let token = System.BitConverter.ToInt32(il, offset) let mb = ty.Module.ResolveMethod(token) match Expr.TryGetReflectedDefinition mb with | Some ex -> printfn "success %A" e | None -> failwith "failed"它运行良好,即使 f 是在另一个程序集 (.dll) 中或在 Foo 调用发生的地方定义的.它还不是完全通用的,因为我必须定义 argty 是什么,但我确信我可以编写一个函数来完成它.
It works well, even if f is defined in another assembly (.dll) or in the same where the call of Foo happens. It's not fully general yet since I have to define what argty is, but I'm sure I can write a function that does it.
事实证明,在编写此代码后,Dustin 对同一问题有类似的解决方案,尽管是在 C# 中(参见 此处).
Turns out after writing this code that Dustin have a similar solution for the same issue, albeit in C# (see it here).
所以这是一个用法示例:
So here's an usage example:
open System open Microsoft.FSharp.Quotations [<ReflectedDefinition>] let F (sv:int) (a:int[]) (b:int[]) (c:int[]) (d:int[]) = let temp = a.[2] + b.[3] c.[0] <- temp () let Foo (f:S -> A[] -> B[] -> C[] -> D[] -> unit) = let ty = f.GetType() let arr = ty.BaseType.GetGenericArguments() let argty = Array.init (arr.Length-1) (fun i -> arr.[i]) let mi = ty.GetMethod("Invoke", argty) let il = mi.GetMethodBody().GetILAsByteArray() let offset = 9 let token = System.BitConverter.ToInt32(il, offset) let mb = ty.Module.ResolveMethod(token) mb let main () = let mb = Foo F printfn "%s" mb.Name match Expr.TryGetReflectedDefinition mb with | None -> () | Some(e) -> printfn "%A" e do main ()它的作用是打印 F 的名称,如果函数是反射定义,则打印其 AST.
What it does is printing name of F, and its AST if the function is a reflected definition.
但是经过进一步调查,恰好这个hack只在调试模式下有效(并且F必须是一个函数值以及一个顶级定义),所以不妨说它是一个不可能做的事情.
But after further investigation, it happens that this hack only works in debug mode (and F has to be a function value as well as a top level definition), so might as well say that it's an impossible thing to do.
以下是调试/发布版本中 FSharpFunc 的 Invoke 方法的 IL 代码:
Here's the IL code of the FSharpFunc's Invoke method in both debug/release build:
调试模式:
.method /*06000007*/ public strict virtual instance class [FSharp.Core/*23000002*/]Microsoft.FSharp.Core.Unit/*01000006*/ Invoke(int32 sv, int32[] a, int32[] b, int32[] c, int32[] d) cil managed // SIG: 20 05 12 19 08 1D 08 1D 08 1D 08 1D 08 { // Method begins at RVA 0x21e4 // Code size 16 (0x10) .maxstack 9 IL_0000: /* 00 | */ nop IL_0001: /* 03 | */ ldarg.1 IL_0002: /* 04 | */ ldarg.2 IL_0003: /* 05 | */ ldarg.3 IL_0004: /* 0E | 04 */ ldarg.s c IL_0006: /* 0E | 05 */ ldarg.s d IL_0008: /* 28 | (06)000001 */ call void Program/*02000002*/::F(int32, int32[], int32[], int32[], int32[]) /* 06000001 */ IL_000d: /* 00 | */ nop IL_000e: /* 14 | */ ldnull IL_000f: /* 2A | */ ret } // end of method mb@25::Invoke释放模式:
method public strict virtual instance class [FSharp.Core]Microsoft.FSharp.Core.Unit Invoke(int32 sv, int32[] a, int32[] b, int32[] c, int32[] d) cil managed { // Code size 28 (0x1c) .maxstack 7 .locals init ([0] int32 V_0) IL_0000: nop IL_0001: ldarg.2 IL_0002: ldc.i4.2 IL_0003: ldelem [mscorlib]System.Int32 IL_0008: ldarg.3 IL_0009: ldc.i4.3 IL_000a: ldelem [mscorlib]System.Int32 IL_000f: add IL_0010: stloc.0 IL_0011: ldarg.s c IL_0013: ldc.i4.0 IL_0014: ldloc.0 IL_0015: stelem [mscorlib]System.Int32 IL_001a: ldnull IL_001b: ret } // end of method mb@25::Invoke可以看到,在release模式下,编译器将F的代码内联到Invoke方法中,所以调用F的信息(以及获取token的可能性)都没有了..
You can see that in release mode, the compiler inlines code of F into the Invoke method, so the information of calling F (and the possibility to retrieve the token) is gone..
更多推荐
检索 F# 函数的 MethodInfo
发布评论