views:

170

answers:

1

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?

+8  A: 

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
kvb
+1, Tested and works.
gradbot