2009-10-15 57 views
11

我想編寫一個函數,它將函數f作爲參數並返回與f關聯的System.Reflection.MethodInfo。檢索F#函數的MethodInfo

我不太確定這是否可行。

+0

您打算如何處理MethodInfo? – Brian 2009-10-15 22:26:15

+0

我試圖得到反映的定義,與呃..TryGetReflectedDefinition函數。 – Stringer 2009-10-15 22:36:39

+1

我不知道F#中的任何內容,但在o'caml中,您可以使用預處理器(我不知道F#中是否有類似內容)http://groups.google.com/group/fa。 caml/browse_thread/thread/25c9706b89196140 – LB40 2009-10-26 00:20:34

回答

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" 

它運作良好,即使f爲在另一組件(.DLL)或在富的呼叫發生相同的定義。 這不是完全一般的,因爲我必須定義argty是什麼,但我確信我可以寫一個函數來完成它。

在編寫此代碼後發現達斯汀對於同一問題具有類似的解決方案,儘管在C#中(請參閱here)。

編輯: 因此,這裏是一個使用示例:

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必須是一個函數值以及一個頂級的定義),所以還不如說它是一個不可能事要做

這裏的FSharpFunc的Invoke方法在兩個調試/發行版本的IL代碼:

調試模式:

.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 

你可以看到,在發行模式中,編譯器將F的代碼嵌入到Invoke方法中,因此調用F的信息(以及檢索令牌的可能性)消失了。

+0

如果這對你有用,你會想要接受它作爲答案。 – kersny 2009-10-17 16:46:53

+0

你能舉一個用法例子嗎?我理解解決方案的總體思路,但我不明白爲什麼f有它的類型。 – 2009-10-19 13:55:36

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 2009-10-15 22:25:53

1

我不知道是否有任何種類的功能,一般的答案,但如果你的函數是簡單的( 'A - >' b),那麼你可以寫

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

+0

謝謝,我已經嘗試過,但似乎沒有工作.. – Stringer 2009-10-15 22:50:05

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 2009-10-17 15:59:01