Je voudrais écrire une fonction qui prend une fonction f en argument et retourne le System.Reflection.MethodInfo associé à f.Récupérer MethodInfo d'une fonction F #
Je ne sais pas si c'est faisable ou non.
Je voudrais écrire une fonction qui prend une fonction f en argument et retourne le System.Reflection.MethodInfo associé à f.Récupérer MethodInfo d'une fonction F #
Je ne sais pas si c'est faisable ou non.
Donc, j'ai finalement trouvé une solution. Très hacky, mais bon! Ça marche! (éditer: en mode débogage seulement).
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"
Il fonctionne bien, même si f est définie dans un autre ensemble (.dll) ou dans le même où l'appel de Foo arrive. Ce n'est pas encore général car je dois définir ce qu'est l'argty, mais je suis sûr que je peux écrire une fonction qui le fait.
Il s'avère après avoir écrit ce code que Dustin a une solution similaire pour le même problème, mais en C# (voir here).
EDIT: est un exemple Voici d'utilisation:
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()
Ce qu'il fait est le nom d'impression de F, et son AST si la fonction est une définition réfléchie.
Mais après enquête, il arrive que ce hack fonctionne uniquement en mode débogage (et F doit être une valeur de fonction ainsi qu'une définition de haut niveau), donc peut aussi bien dire qu'il est un impossible chose faire.
est ici le code IL de la méthode Invoke du FSharpFunc dans les deux debug/release build:
mode DEBUG:
.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
mode 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
Vous pouvez voir que dans la version mode, le compilateur inline le code de F dans la méthode Invoke, donc les informations d'appel F (et la possibilité de récupérer le jeton) ont disparu.
Si cela fonctionne pour vous, vous allez vouloir l'accepter comme réponse. – kersny
Pourriez-vous donner un exemple d'utilisation? Je comprends l'idée générale de la solution, mais je ne vois pas pourquoi f a le type qu'il a. –
Ce n'est pas (facilement) possible. La chose à noter est que lorsque vous écrivez:
let printFunctionName f =
let mi = getMethodInfo f
printfn "%s" mi.Name
paramètre 'f' est simplement une instance de type FSharpFunc < ,>. Ainsi, les éléments suivants sont possibles:
printFunctionName (fun x -> x + 1) // Lambda expression
printFunctionName String.ToUpper // Function value
printFunctionName (List.map id) // Curried function
printFunctionNAme (not >> List.empty) // Function composition
Dans les deux cas, il n'y a pas de réponse simple à cette
Peut-être que cela aide, je sais que f est toujours une valeur de fonction. Que recommandez-vous? Je vais prendre n'importe quel hack .. – Stringer
Je ne sais pas s'il y a une réponse générale pour tout type de fonction, mais si votre fonction est simple ('a ->' b) vous pouvez écrire
let getMethodInfo (f : 'a -> 'b) = (FastFunc.ToConverter f).Method
Merci, je l'ai essayé, mais ne semble pas fonctionner .. – Stringer
le programme ci-dessous aide?
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()
Ouais à droite, à peu près comme ça, s'attendre à ne pas connaître le nom de la méthode ("F") ou le module . – Stringer
Qu'avez-vous l'intention de faire avec MethodInfo? – Brian
J'essaie d'obtenir la définition réfléchie, avec erm ..Fonction TryGetReflectedDefinition. – Stringer
Je ne sais rien dans F # mais dans o'caml vous pouvez le faire en utilisant le pré-processeur (je ne sais pas s'il y a quelque chose de similaire en F #) http://groups.google.com/group/fa. caml/browse_thread/thread/25c9706b89196140 – LB40