Vorrei scrivere una funzione che accetta una funzione f come argomento e restituisce System.Reflection.MethodInfo associato a f.Recupera MethodInfo di una funzione F #
Non sono sicuro se sia possibile o meno.
Vorrei scrivere una funzione che accetta una funzione f come argomento e restituisce System.Reflection.MethodInfo associato a f.Recupera MethodInfo di una funzione F #
Non sono sicuro se sia possibile o meno.
Quindi, ho finalmente trovato una soluzione. Molto hacky, ma hey! Funziona! (modifica: solo in modalità 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"
Funziona bene, anche se f è definito in un altro assembly (DLL) o nello stesso in cui la chiamata di Foo succede. Non è ancora completamente generale dato che devo definire cosa sia argty, ma sono sicuro di poter scrivere una funzione che lo faccia.
Si scopre dopo aver scritto questo codice che Dustin ha una soluzione simile per lo stesso problema, anche se in C# (si veda here).
EDIT: Quindi, ecco un esempio di utilizzo:
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()
Ciò che fa è il nome di F, e la sua AST stampa se la funzione è una definizione riflessa.
Ma dopo ulteriori indagini, accade che questo hack funziona solo in modalità debug (ed F deve essere un valore di funzione così come una definizione di livello superiore), così potrebbe anche dire che è un impossibile cosa fare.
Ecco il codice IL di metodo Invoke della FSharpFunc sia build di debug/release:
modalità DEBUG: modalità
.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
STAMPA:
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
Si può vedere che in rilascio modalità, il compilatore inline codice di F nel metodo Invoke, quindi le informazioni di chiamata F (e la possibilità di recuperare il token) sono scomparse ..
Se questo funziona per te, vorrai accettarlo come risposta. – kersny
Puoi dare un esempio di utilizzo? Capisco l'idea generale della soluzione, ma non vedo perché f ha il tipo che ha. –
Questo non è (facilmente) possibile. La cosa da notare è che quando si scrive:
let printFunctionName f =
let mi = getMethodInfo f
printfn "%s" mi.Name
Parametro 'f' è semplicemente un esempio di tipo FSharpFunc < ,>. Così i seguenti sono tutti i possibili:
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 entrambi i casi non v'è alcuna risposta diretta a questa
Forse questo aiuta, so che f è sempre un valore di funzione. Cosa raccomandi? Prenderò qualche trucco .. – Stringer
Non so se c'è una risposta generale per qualsiasi tipo di funzione, ma se la vostra funzione è semplice ('a ->' b), allora si potrebbe scrivere
let getMethodInfo (f : 'a -> 'b) = (FastFunc.ToConverter f).Method
Grazie, l'ho provato, ma non sembra funzionare .. – Stringer
fa il seguente programma di aiuto?
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()
Sì, giusto, più o meno così, mi aspetto che non conosca il nome del metodo ("F") o il modulo . – Stringer
Cosa intendi fare con MethodInfo? – Brian
Cerco di ottenere la definizione riflessa, con erm ..Funzione TryGetReflectedDefinition. – Stringer
Non so nulla in F # ma in o'caml puoi farlo usando il pre-processore (non so se c'è qualcosa di simile in F #) http://groups.google.com/group/fa. caml/browse_thread/thread/25c9706b89196140 – LB40