如何将System.Reflection.MemberInfo值转换为它代表的F#值?

时间:2016-01-08 16:18:14

标签: generics reflection f# system.reflection

我试图在运行时加载并执行F#代码。 FSharp.Compiler.Service程序集为程序中的每个值(或函数)定义生成System.Reflection.MemberInfo值。

我大致试图实施以下内容:

cast<'a> : System.Reflection.MemberInfo -> 'a

如果MemberInfo值表示类型'a的值(并返回该值),则成功;否则失败。

1 个答案:

答案 0 :(得分:0)

首先,你的函数需要有一个可选的结果 - 表明它可能并不总是成功。

然后,成员可以是(a)嵌套类型,(b)属性,(c)字段和(d)方法。我猜你只对后三种感兴趣。

你需要分别处理这三种情况,因为它们实际上是不同的东西,而不是三种相同的东西。对于字段和属性,您可以分别使用FieldInfo.GetValuePropertyInfo.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表示方法。不要忘记缓存已编译的委托,否则会更慢: - )