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

时间:2017-03-08 06:55:30

标签: go reflection private-members

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

请注意,reflect.DeepEqual工作得很好(也就是说,它可以访问未导出的字段),但我无法对该功能进行正面或反面操作。这是一个可以显示实际效果的游戏区: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
}

3 个答案:

答案 0 :(得分:12)

如果结构是可寻址的,您可以使用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.

See full example on the playground.

unsafe.Pointer的使用是有效的"根据{{​​1}}包的文档,以及unsafe的干净利落。

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

go vet

See full example on the playground.

答案 1 :(得分:3)

基于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))
}


reflect.NewAt乍一看可能会令人困惑。它使用reflect.Value作为指针,返回代表指定field.Type()值的指针的unsafe.Pointer(field.UnsafeAddr())。在这种情况下,reflect.NewAtreflect.New不同,type Foo struct { unexportedField string } GetUnexportedField(reflect.ValueOf(&Foo{}).Elem().FieldByName("unexportedField")) 将返回指向新初始化值的指针。

示例:

1

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

答案 2 :(得分:2)

reflect.DeepEqual()可以执行此操作,因为它可以访问reflect包的未导出功能,在这种情况下,即valueInterface()函数,它接受safe参数,如果safe=true,则拒绝通过Value.Interface()方法访问未导出的字段值。 reflect.DeepEqual()将(可能)称之为传递safe=false

您仍然可以执行此操作,但不能将Value.Interface()用于未导出的字段。相反,您必须使用特定于类型的方法,例如stringValue.Interface(),浮点数为Value.String(),int为Value.Float()等。这些将返回值的副本(这足以检查它),但不允许你修改字段的值(如果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()) } 可行,那么可能是"部分"可能是字段类型指针类型)。

如果字段碰巧是接口类型,您可以使用Value.Int()来获取接口值包含/包装的值。

演示:

string hello
int64 2
float64 3

输出(在Value.Elem()上尝试):

    RestTemplate rest = new RestTemplate();
    String responseEntity;
    String uri = "http://...";
    responseEntity = rest.postForObject(uri, null, String.class);
    System.out.println(responseEntity);