gpt4 book ai didi

reflection - 检索 F# 函数的 MethodInfo

转载 作者:行者123 更新时间:2023-12-03 07:13:57 25 4
gpt4 key购买 nike

我想编写一个函数,它接受函数 f 作为参数并返回与 f 关联的 System.Reflection.MethodInfo。

不太确定是否可行。

最佳答案

所以,我终于找到了解决办法。非常hacky,但是嘿!有用! (编辑:仅在 Debug模式下)。

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 是什么,但我确信我可以编写一个函数来完成它。

事实证明,在编写此代码后,达斯汀对同一问题有类似的解决方案,尽管是用 C# 编写的(参见 here )。

编辑:这是一个使用示例:

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(如果该函数是反射定义)。

但是经过进一步调查,这个 hack 恰好只在 Debug模式下有效(并且 F 必须是一个函数值以及顶级定义),所以不妨说它是一个 不可能做的事情

以下是调试/发布版本中 FSharpFunc 的 Invoke 方法的 IL 代码:

Debug模式:

.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模式:

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 的可能性)消失了。

关于reflection - 检索 F# 函数的 MethodInfo,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1575180/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com