检索 F# 函数的 MethodInfo [英] Retrieve MethodInfo of a F# function

查看:20
本文介绍了检索 F# 函数的 MethodInfo的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想编写一个函数,将函数 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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆