Я хотел бы написать функцию, которая принимает функцию f в качестве аргумента и возвращает System.Reflection.MethodInfo, связанный с f.Retrieve MethodInfo функции F #
Я не совсем уверен, насколько это возможно или нет.
Я хотел бы написать функцию, которая принимает функцию f в качестве аргумента и возвращает System.Reflection.MethodInfo, связанный с f.Retrieve MethodInfo функции F #
Я не совсем уверен, насколько это возможно или нет.
Итак, я наконец нашел решение. Очень хаки, но эй! Оно работает! (отредактируйте: только в режиме отладки).
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 (и возможность извлечь токен) исчезла.
Если это сработает для вас, вы захотите принять его в качестве ответа. – kersny
Не могли бы вы привести пример использования? Я понимаю общую идею решения, но я не понимаю, почему у f есть тот тип, который у него есть. –
Это невозможно (легко). Дело в том, следует отметить, что, когда вы пишете:
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
В любом случае нет однозначного ответа на этот
Возможно, это помогает, я знаю, что f всегда является значением функции. Что вы посоветовали? Я возьму любой хак .. – Stringer
Я не знаю, есть ли вообще ответ на какой-либо функции, но если ваша функция простой ('а ->' б), то вы можете написать
let getMethodInfo (f : 'a -> 'b) = (FastFunc.ToConverter f).Method
Спасибо, я пробовал, но, похоже, не работает. – Stringer
ли программу ниже помощи?
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()
Да, в общем, похоже, что я не знаю имени метода («F») или модуля , – Stringer
Что вы собираетесь делать с MethodInfo? – Brian
Я пытаюсь получить отраженное определение, с erm ..Функция TryGetReflectedDefinition. – Stringer
Я ничего не знаю в F #, но в o'caml вы можете сделать это, используя предварительный процессор (я не знаю, есть ли что-то подобное в F #) http://groups.google.com/group/fa. caml/browse_thread/thread/25c9706b89196140 – LB40