2009-10-15 14 views
11

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.

+0

Cosa intendi fare con MethodInfo? – Brian

+0

Cerco di ottenere la definizione riflessa, con erm ..Funzione TryGetReflectedDefinition. – Stringer

+1

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

risposta

5

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 ..

+0

Se questo funziona per te, vorrai accettarlo come risposta. – kersny

+0

Puoi dare un esempio di utilizzo? Capisco l'idea generale della soluzione, ma non vedo perché f ha il tipo che ha. –

2

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

+0

Forse questo aiuta, so che f è sempre un valore di funzione. Cosa raccomandi? Prenderò qualche trucco .. – Stringer

1

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

+0

Grazie, l'ho provato, ma non sembra funzionare .. – Stringer

3

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()  
+0

Sì, giusto, più o meno così, mi aspetto che non conosca il nome del metodo ("F") o il modulo . – Stringer

Problemi correlati