Chciałbym napisać funkcję, która przyjmuje funkcję f jako argument i zwraca System.Reflection.MethodInfo związane z f.Pobierz metodeInfo funkcji F #
Nie jestem do końca pewien, czy jest to wykonalne, czy nie.
Chciałbym napisać funkcję, która przyjmuje funkcję f jako argument i zwraca System.Reflection.MethodInfo związane z f.Pobierz metodeInfo funkcji F #
Nie jestem do końca pewien, czy jest to wykonalne, czy nie.
W końcu znalazłem rozwiązanie. Bardzo hacky, ale hej! To działa! (edytuj: tylko w trybie debugowania).
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"
Działa dobrze, nawet jeśli f jest zdefiniowany w innym zestawie (.dll) lub w tym samym, w którym następuje wywołanie Foo. To nie jest w pełni ogólne, ponieważ muszę zdefiniować, co jest argty, ale jestem pewien, że mogę napisać funkcję, która to robi.
Po napisaniu tego kodu okazuje się, że Dustin ma podobne rozwiązanie dla tego samego problemu, aczkolwiek w języku C# (zobacz: here).
EDIT: Tak oto przykład użycia:
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()
Co robi drukuje nazwę F i jego AST jeśli funkcja jest odzwierciedlenie definicji.
Ale po dalszym dochodzeniu, zdarza się, że ten hack, działa tylko w trybie debugowania (i F musi być wartością funkcji, a także definicji najwyższego poziomu), więc równie dobrze można powiedzieć, że jest to niemożliwe rzecz zrobić.
Oto kod IL o metodzie FSharpFunc powołują się zarówno w budowie debug/release:
Debug Mode: Tryb
.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 [email protected]::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 [email protected]::Invoke
Można zobaczyć, że w wydaniu tryb kompilator dodaje kod F do metody Invoke, więc informacja o wywołaniu F (i możliwości odzyskania tokena) zniknęła ..
Jeśli to działa dla Ciebie, będziesz chciał zaakceptować to jako odpowiedź. – kersny
Czy możesz podać przykład użycia? Rozumiem ogólną ideę rozwiązania, ale nie rozumiem, dlaczego f ma taki typ, jaki ma. –
Nie jest to (łatwo) możliwe. Chodzi o to, aby pamiętać, że kiedy piszesz:
let printFunctionName f =
let mi = getMethodInfo f
printfn "%s" mi.Name
parametru 'F' jest po prostu instancją typu FSharpFunc < ,>. Oto więc wszystko jest możliwe:
printFunctionName (fun x -> x + 1) // Lambda expression
printFunctionName String.ToUpper // Function value
printFunctionName (List.map id) // Curried function
printFunctionNAme (not >> List.empty) // Function composition
W obu przypadkach nie jest prosta odpowiedź na to
Może to pomaga, wiem, że f jest zawsze wartością funkcji. Co polecasz? Wezmę każdy hack .. – Stringer
ja nie wiem, czy istnieje ogólna odpowiedź dla każdego rodzaju funkcji, ale jeśli funkcja jest proste ('a ->' b) następnie można napisać
let getMethodInfo (f : 'a -> 'b) = (FastFunc.ToConverter f).Method
Dzięki, próbowałem go, ale nie wydaje się działać .. – Stringer
Czy program poniżej pomoc?
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()
Tak, prawie tak, oczekuję, że nie znam nazwy metody ("F") lub modułu . – Stringer
Co zamierzacie zrobić z MethodInfo? – Brian
Próbuję uzyskać odzwierciedlenie definicji, z erm ..Funkcja TryGetReflectedDefinition. – Stringer
Nie wiem nic w F # ale w o'caml można to zrobić za pomocą pre-procesora (nie wiem, czy jest coś podobnego w F #) http://groups.google.com/group/fa. caml/browse_thread/thread/25c9706b89196140 – LB40