2009-10-15 10 views
11

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.

+0

Qu'avez-vous l'intention de faire avec MethodInfo? – Brian

+0

J'essaie d'obtenir la définition réfléchie, avec erm ..Fonction TryGetReflectedDefinition. – Stringer

+1

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

Répondre

5

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.

+0

Si cela fonctionne pour vous, vous allez vouloir l'accepter comme réponse. – kersny

+0

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

2

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

+0

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

1

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

+0

Merci, je l'ai essayé, mais ne semble pas fonctionner .. – Stringer

3

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

Ouais à droite, à peu près comme ça, s'attendre à ne pas connaître le nom de la méthode ("F") ou le module . – Stringer