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.
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.
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.
Si esto funciona para usted, querrá aceptarlo como la respuesta. – kersny
¿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. –
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
Quizás esto ayude, sé que f siempre es un valor de Función. ¿Qué recomiendas? Tomaré cualquier truco ... – Stringer
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
Gracias, lo he intentado, pero parece que no funciona ... – Stringer
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()
Sí, casi así, espero no saber el nombre del método ("F") o el módulo . – Stringer
¿Qué piensas hacer con MethodInfo? – Brian
Intento obtener la definición reflejada, con erm ..Función TryGetReflectedDefinition. – Stringer
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