如何使用f#interactive访问私有字段/方法/属性

boh*_*nko 4 f# private-members f#-interactive

F#interactive是一个功能强大的开发工具,因为它允许运行WinForm或Wpf窗口在那里调用任意代码.

这为"先试后试"方法提供了一种方法.

我经常希望明确地"突破界限"

  • 调用私有/受保护的方法
  • 访问/更改私人字段和属性

是否有解决方法来实现这一目标?

kvb*_*kvb 8

FSI没有为此提供任何特定支持,但您可以使用Reflection来执行您想要的操作.

open System.Reflection
let field = typeof<MyType>.GetField("fieldName", BindingFlags.NonPublic ||| BindingFlags.Instance)
field.SetValue(myInstance, newVal)
Run Code Online (Sandbox Code Playgroud)

您可以更进一步,定义方法或运算符,以使这更容易.例如,您可以设置F#的动态赋值运算符以分配给私有字段:

let (?<-) o s v = 
  let field = (o.GetType()).GetField(s, BindingFlags.NonPublic ||| BindingFlags.Instance)
  field.SetValue(o,v)

myInstance?fieldName <- newVal (* Note: no quotes around fieldName here *)
Run Code Online (Sandbox Code Playgroud)

以下是解决公共或私有字段,属性或方法的一些原始代码.需要注意的是有很多的方法,使这将失败(特别是,试图把它用在重载的方法将无法正常工作).

open System
open System.Reflection
open Microsoft.FSharp.Reflection

type DynamicHelper =  
  static member MkMethod<'t,'u> (mi:MethodInfo) o : 't -> 'u=
    let typ = typeof<'t>
    fun t -> 
      let args = 
        if (typ = typeof<unit>) then [||]
        else
          if not (FSharpType.IsTuple typ) then [| box t |]
          else
            FSharpValue.GetTupleFields t
      mi.Invoke(o, args) :?> 'u

let (?) (o:'a) s : 'b =
  let ty = o.GetType()
  let field = ty.GetField(s, BindingFlags.Instance ||| BindingFlags.Public ||| BindingFlags.NonPublic)
  if field <> null then field.GetValue(o) :?> 'b
  else
    let prop = ty.GetProperty(s, BindingFlags.Instance ||| BindingFlags.Public ||| BindingFlags.NonPublic)
    if prop <> null then prop.GetValue(o, null) :?> 'b
    else
      let meth = ty.GetMethod(s, BindingFlags.Instance ||| BindingFlags.Public ||| BindingFlags.NonPublic)
      let d,r = FSharpType.GetFunctionElements(typeof<'b>)
      typeof<DynamicHelper>.GetMethod("MkMethod").MakeGenericMethod([|d;r|]).Invoke(null, [| box meth; box o |]) :?> 'b
Run Code Online (Sandbox Code Playgroud)

有了这个,您可以动态调用方法和属性:

let (t:System.Type) = "test"?GetType()?BaseType
Run Code Online (Sandbox Code Playgroud)