代码之家  ›  专栏  ›  技术社区  ›  bohdan_trotsenko

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

  •  4
  • bohdan_trotsenko  · 技术社区  · 16 年前

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

    这为“先尝试再编码”的方法提供了一种途径。

    很多时候,我希望明确地、明确地“打破界限”

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

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

    1 回复  |  直到 13 年前
        1
  •  9
  •   kvb    16 年前

    FSI没有为此提供任何特定的支持,但是您可以使用反射来做您想要做的事情。

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

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

    下面是一些用于解析公共或私有字段、属性或方法的粗略代码。请注意,有很多方法会失败(特别是,尝试在重载方法上使用它是行不通的)。

    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
    

    使用此选项,您可以动态调用方法和属性,如下所示:

    let (t:System.Type) = "test"?GetType()?BaseType
    
    推荐文章