[英]Retrieve MethodInfo of a F# function
I would like to write a function that takes a function f as an argument and returns the System.Reflection.MethodInfo associated to f. 我想编写一个函数,它将函数f作为参数,并返回与f关联的System.Reflection.MethodInfo。
I'm not quite sure if it is feasible or not. 我不太确定它是否可行。
So, I finally found a solution. 所以,我终于找到了解决方案。 Very hacky, but hey!
非常hacky,但嘿! 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"
It works well, even if f is defined in another assembly (.dll) or in the same where the call of Foo happens. 它运行良好,即使f在另一个程序集(.dll)中定义,或者在Foo调用发生时也是如此。 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.
它还不完全通用,因为我必须定义argty是什么,但我确信我可以写一个函数来完成它。
Turns out after writing this code that Dustin have a similar solution for the same issue, albeit in C# (see it here ). 在编写此代码之后,Dustin对同一问题有类似的解决方案,尽管在C#中(见此处 )。
EDIT: 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 ()
What it does is printing name of F, and its AST if the function is a reflected definition. 它的作用是打印F的名称,如果函数是反射定义,则打印其AST。
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 . 但经过进一步调查后,碰巧这个hack只能在调试模式下工作(F必须是函数值和顶级定义),所以不妨说这是不可能的事情 。
Here's the IL code of the FSharpFunc's Invoke method in both debug/release build: 这是调试/发布版本中FSharpFunc的Invoke方法的IL代码:
DEBUG mode: 调试模式:
.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
RELEASE mode: 发布模式:
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
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的代码内联到Invoke方法中,因此调用F的信息(以及检索令牌的可能性)消失了。
Does the program below help? 以下程序有帮助吗?
module Program
[<ReflectedDefinition>]
let F x =
x + 1
let Main() =
let x = F 4
let a = System.Reflection.Assembly.GetExecutingAssembly()
let modu = a.GetType("Program")
let methodInfo = modu.GetMethod("F")
let reflDefnOpt = Microsoft.FSharp.Quotations.Expr.TryGetReflectedDefinition(methodInfo)
match reflDefnOpt with
| None -> printfn "failed"
| Some(e) -> printfn "success %A" e
Main()
This is not (easily) possible. 这不是(容易)可能的。 The thing to note is that when you write:
要注意的是,当你写:
let printFunctionName f =
let mi = getMethodInfo f
printfn "%s" mi.Name
Parameter 'f' is simply an instance of type FSharpFunc< , >. 参数'f'只是FSharpFunc < , >类型的一个实例。 So the following are all possible:
所以以下都是可能的:
printFunctionName (fun x -> x + 1) // Lambda expression
printFunctionName String.ToUpper // Function value
printFunctionName (List.map id) // Curried function
printFunctionNAme (not >> List.empty) // Function composition
In either case there is no straightforward answer to this 无论哪种情况,都没有直截了当的答案
我不知道对于任何类型的函数是否有一般答案,但如果你的函数很简单('a - >'b)那么你可以写
let getMethodInfo (f : 'a -> 'b) = (FastFunc.ToConverter f).Method
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.