在golang/reflect中访问未导出的字段?

U A*_*los 9 reflection go private-members

有没有办法使用Reflect访问go 1.8中未导出的字段?这似乎不再起作用:https://stackoverflow.com/a/17982725/555493

请注意,reflect.DeepEqual工作正常(也就是说,它可以访问未导出的字段),但我无法做出该功能的正面或反面.这是一个go playarea,它显示了它的实际效果:https://play.golang.org/p/vyEvay6eVG.src代码如下

import (
"fmt"
"reflect"
)

type Foo struct {
  private string
}

func main() {
    x := Foo{"hello"}
    y := Foo{"goodbye"}
    z := Foo{"hello"}

    fmt.Println(reflect.DeepEqual(x,y)) //false
    fmt.Println(reflect.DeepEqual(x,z)) //true
}
Run Code Online (Sandbox Code Playgroud)

cpc*_*len 14

如果结构是可寻址的,您可以使用它unsafe.Pointer来访问它(读或写)字段,如下所示:

rs := reflect.ValueOf(&MyStruct).Elem()
rf := rs.Field(n)
// rf can't be read or set.
rf = reflect.NewAt(rf.Type(), unsafe.Pointer(rf.UnsafeAddr())).Elem()
// Now rf can be read and set.
Run Code Online (Sandbox Code Playgroud)

在操场上看到完整的例子.

unsafe.Pointer根据unsafe包的文档,这种使用是"有效的" ,go vet干净利落.

如果结构不可寻址,这个技巧将不起作用,但你可以创建一个这样的可寻址副本:

rs = reflect.ValueOf(MyStruct)
rs2 := reflect.New(rs.Type()).Elem()
rs2.Set(rs)
rf = rs2.Field(0)
rf = reflect.NewAt(rf.Type(), unsafe.Pointer(rf.UnsafeAddr())).Elem()
// Now rf can be read.  Setting will succeed but only affects the temporary copy.
Run Code Online (Sandbox Code Playgroud)

在操场上看到完整的例子.

  • @rakim:这意味着您可以获取它的地址(例如,它是局部变量或堆上的地址),而不是临时值(例如函数的返回值)。您可以在这里阅读更多信息:https://utcc.utoronto.ca/~cks/space/blog/programming/GoAddressableValues (2认同)

mat*_*tes 9

基于cpcallen的工作:

import (
    "reflect"
    "unsafe"
)

func GetUnexportedField(field reflect.Value) interface{} {
    return reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem().Interface()
}

func SetUnexportedField(field reflect.Value, value interface{}) {
    reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).
        Elem().
        Set(reflect.ValueOf(value))
}

Run Code Online (Sandbox Code Playgroud)

reflect.NewAt刚开始阅读可能会感到困惑。它返回一个reflect.Value表示指向指定值的指针field.Type()unsafe.Pointer(field.UnsafeAddr())用作该指针。在此上下文reflect.NewAt中不同于reflect.New,后者将返回一个指向新初始化值的指针。

例子:

type Foo struct {
    unexportedField string
}

GetUnexportedField(reflect.ValueOf(&Foo{}).Elem().FieldByName("unexportedField"))
Run Code Online (Sandbox Code Playgroud)

https://play.golang.org/p/IgjlQPYdKFR


icz*_*cza 5

reflect.DeepEqual()可以这样做,因为它可以访问reflect包的未导出功能,在这种情况下,即valueInterface()函数,它接受一个safe参数,拒绝通过Value.Interface()方法 if访问未导出的字段值safe=truereflect.DeepEqual()将(可能)称为传递safe=false.

您仍然可以这样做,但不能Value.Interface()用于未导出的字段。相反,您必须使用特定于类型的方法,例如Value.String()for stringValue.Float()浮点数、Value.Int()整数等。这些将返回值的副本(足以检查它),但不允许您修改字段的值(如果Value.Interface()可以工作并且字段类型是指针类型,这可能是“部分”可能的)。

如果字段恰好是接口类型,则可以使用Value.Elem()来获取接口值包含/包装的值。

展示:

type Foo struct {
    s string
    i int
    j interface{}
}

func main() {
    x := Foo{"hello", 2, 3.0}
    v := reflect.ValueOf(x)

    s := v.FieldByName("s")
    fmt.Printf("%T %v\n", s.String(), s.String())

    i := v.FieldByName("i")
    fmt.Printf("%T %v\n", i.Int(), i.Int())

    j := v.FieldByName("j").Elem()
    fmt.Printf("%T %v\n", j.Float(), j.Float())
}
Run Code Online (Sandbox Code Playgroud)

输出(在Go Playground上试试):

string hello
int64 2
float64 3
Run Code Online (Sandbox Code Playgroud)