Search code examples
f#private-membersf#-interactive

How to access private fields/methods/properties using f# interactive


F# interactive is a powerful development tool as it allows to run either WinForm or Wpf window and invoke arbitrary code in there.

This gives a way for a 'try-before-you code' approach.

Very often I wish to 'break the boundaries' explicitly and

  • invoke private/protected methods
  • access/change private fields and properties

Is there a workaround to achieve this?


Solution

  • FSI doesn't provide any particular support for this, but you can use Reflection to do the things you want.

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

    You can go further and define methods or operators to make this even easier. For instance you can set up F#'s dynamic assignment operator to assign to private fields:

    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 *)
    

    Here's some crude code to resolve public or private fields, properties, or methods. Note that there are plenty of ways in which this will fail (in particular, trying to use it on overloaded methods will not work).

    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
    

    With this you can dynamically invoke methods and properties as such:

    let (t:System.Type) = "test"?GetType()?BaseType