我试图在运行时加载并执行F#代码。 FSharp.Compiler.Service
程序集为程序中的每个值(或函数)定义生成System.Reflection.MemberInfo
值。
我大致试图实施以下内容:
cast<'a> : System.Reflection.MemberInfo -> 'a
如果MemberInfo
值表示类型'a
的值(并返回该值),则成功;否则失败。
答案 0 :(得分:0)
首先,你的函数需要有一个可选的结果 - 表明它可能并不总是成功。
然后,成员可以是(a)嵌套类型,(b)属性,(c)字段和(d)方法。我猜你只对后三种感兴趣。
你需要分别处理这三种情况,因为它们实际上是不同的东西,而不是三种相同的东西。对于字段和属性,您可以分别使用FieldInfo.GetValue
和PropertyInfo.GetValue
来获取值。对于方法,您可以使用MethodInfo.Invoke
来调用它并返回结果。
大致是:
let getValue<'a> (o: obj) (m: MemberInfo): 'a option =
match m with
| :? FieldInfo as f when f.FieldType = typeof<'a> ->
Some( f.GetValue o :?> 'a )
| :? PropertyInfo as p when p.PropertyType = typeof<'a> && p.GetIndexParameters().Length = 0 ->
Some( p.GetValue( o, [||] ) :?> 'a )
| _ -> None
let getFunc<'a, 'b> (o: obj) (m: MemberInfo): ('a -> 'b) option =
match m with
// F# compiler may compile some functions as properties of type FSharpFunc<_>, so need to cover that
| :? FieldInfo -> getValue<'a -> 'b> o m
| :? PropertyInfo -> getValue<'a -> 'b> o m
// If it's a real method of the right type, create a closure to call it
| :? MethodInfo as mt
when mt.ReturnType = typeof<'b> &&
mt.GetParameters().Length = 1 &&
mt.GetParameters().[0].ParameterType = typeof<'a> ->
Some <| fun (a: 'a) -> mt.Invoke( o, [| a :> obj |] ) :?> 'b
// Otherwise, can't produce result
| _ -> None
但是,这些动态调用会非常慢。从你的问题来看,我假设你正在尝试做一些像REPL这样的事情,这样就可以了,但是如果你想多次使用这个,那么一个更好的方法可能是创建和编译LINQ表达式使用{/ 1}}表示字段/属性,Expression.MakeMemberAccess
表示方法。不要忘记缓存已编译的委托,否则会更慢: - )