2009-10-15 15 views
11

Me gustaría escribir una función que toma una función f como argumento y devuelve el System.Reflection.MethodInfo asociado a f.Recuperar método Información de una función F #

No estoy seguro si es factible o no.

+0

¿Qué piensas hacer con MethodInfo? – Brian

+0

Intento obtener la definición reflejada, con erm ..Función TryGetReflectedDefinition. – Stringer

+1

No sé nada en F # pero en o'caml puedes hacerlo usando el pre-procesador (no sé si hay algo similar en F #) http://groups.google.com/group/fa. caml/browse_thread/thread/25c9706b89196140 – LB40

Respuesta

5

Entonces, finalmente encontré una solución. Muy hacky, pero oye! ¡Funciona! (editar: en modo de depuración solamente).

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" 

funciona bien, incluso si f se define en otro ensamblado (.dll) o en el mismo, donde la llamada de Foo sucede. Todavía no es completamente general, ya que tengo que definir qué es argty, pero estoy seguro de que puedo escribir una función que lo haga.

Resulta que después de escribir este código, Dustin tiene una solución similar para el mismo problema, aunque en C# (vea here).

EDIT: Así que aquí es un ejemplo de uso:

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

lo que hace es imprimir el nombre de M, y su AST si la función es una definición reflejada.

Pero después de nuevas investigaciones, sucede que este truco sólo funciona en modo de depuración (y F tiene que ser un valor de la función, así como una definición de nivel superior), lo que también podría decir que es un imposible cosa hacer.

Aquí está el código IL del método Invoke del FSharpFunc tanto en debug/release:

modo de depuración: Modo

.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 

se puede ver que en la liberación modo, el compilador inserta el código de F en el método Invoke, por lo que la información de llamar a F (y la posibilidad de recuperar el token) se ha ido.

+0

Si esto funciona para usted, querrá aceptarlo como la respuesta. – kersny

+0

¿Podría dar un ejemplo de uso? Entiendo la idea general de la solución, pero no veo por qué f tiene el tipo que tiene. –

2

Esto no es (fácilmente) posible. Lo que hay que tener en cuenta es que cuando se escribe:

let printFunctionName f = 
    let mi = getMethodInfo f 
    printfn "%s" mi.Name 

parámetro 'f' es simplemente una instancia de tipo FSharpFunc < ,>. Así que lo siguiente es posible:

printFunctionName (fun x -> x + 1) // Lambda expression 
printFunctionName String.ToUpper  // Function value 
printFunctionName (List.map id)  // Curried function 
printFunctionNAme (not >> List.empty) // Function composition 

En cualquiera de los casos no hay una respuesta directa a esta

+0

Quizás esto ayude, sé que f siempre es un valor de Función. ¿Qué recomiendas? Tomaré cualquier truco ... – Stringer

1

No sé si hay una respuesta general para cualquier tipo de función, pero si su función es sencilla ('a ->' b) a continuación, usted podría escribir

let getMethodInfo (f : 'a -> 'b) = (FastFunc.ToConverter f).Method

+0

Gracias, lo he intentado, pero parece que no funciona ... – Stringer

3

que hace el programa a continuación ayuda?

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í, casi así, espero no saber el nombre del método ("F") o el módulo . – Stringer

Cuestiones relacionadas