2009-10-15 3 views
11

Я хотел бы написать функцию, которая принимает функцию f в качестве аргумента и возвращает System.Reflection.MethodInfo, связанный с f.Retrieve MethodInfo функции F #

Я не совсем уверен, насколько это возможно или нет.

+0

Что вы собираетесь делать с MethodInfo? – Brian

+0

Я пытаюсь получить отраженное определение, с erm ..Функция TryGetReflectedDefinition. – Stringer

+1

Я ничего не знаю в F #, но в o'caml вы можете сделать это, используя предварительный процессор (я не знаю, есть ли что-то подобное в F #) http://groups.google.com/group/fa. caml/browse_thread/thread/25c9706b89196140 – LB40

ответ

5

Итак, я наконец нашел решение. Очень хаки, но эй! Оно работает! (отредактируйте: только в режиме отладки).

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" 

Он хорошо работает, даже если е определяется в другой сборке (.dll) или в том же, где вызов Foo происходит. Это еще не полностью общее, так как я должен определить, что такое argty, но я уверен, что могу написать функцию, которая это делает.

Выходит после написания этого кода, что у Dustin есть аналогичное решение для той же проблемы, хотя и в C# (см. Его here).

EDIT: Так вот пример использования:

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

Что она делает печатает имя F и его AST, если функция отраженная определение.

Но после дальнейшего расследования, случается, что этот хак работает только в режиме отладки (и F должен быть значение функции, а также определение верхнего уровня), так что могли бы также сказать, что что это невозможно вещь сделать.

Вот код IL метода Invoke в FSharpFunc в обоих сборки отладки/выпуска:

режим 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 

РЕЛИЗ:

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 

Вы можете видеть, что в выпуске mode, компилятор вводит код F в метод Invoke, поэтому информация о вызове F (и возможность извлечь токен) исчезла.

+0

Если это сработает для вас, вы захотите принять его в качестве ответа. – kersny

+0

Не могли бы вы привести пример использования? Я понимаю общую идею решения, но я не понимаю, почему у f есть тот тип, который у него есть. –

2

Это невозможно (легко). Дело в том, следует отметить, что, когда вы пишете:

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

Параметр «F» является просто экземпляром типа FSharpFunc < ,>. Таким образом, следующие все возможно:

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

В любом случае нет однозначного ответа на этот

+0

Возможно, это помогает, я знаю, что f всегда является значением функции. Что вы посоветовали? Я возьму любой хак .. – Stringer

1

Я не знаю, есть ли вообще ответ на какой-либо функции, но если ваша функция простой ('а ->' б), то вы можете написать

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

+0

Спасибо, я пробовал, но, похоже, не работает. – Stringer

3

ли программу ниже помощи?

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

Да, в общем, похоже, что я не знаю имени метода («F») или модуля , – Stringer

Смежные вопросы