Go 如何访问未报告的结构字段

Go 如何访问未报告的结构字段,go,reflection,unsafe-pointers,Go,Reflection,Unsafe Pointers,是否有方法使用reflect访问Go 1.8中未报告的字段? 这似乎不再有效: 请注意,reflect.DeepEqual工作得很好(也就是说,它可以访问未报告的字段),但我无法理解该函数的正反两面。这里有一个围棋区,展示了它的动作:。src代码如下所示 import ( "fmt" "reflect" ) type Foo struct { private string } func main() { x := Foo{"hel

是否有方法使用reflect访问Go 1.8中未报告的字段? 这似乎不再有效:

请注意,
reflect.DeepEqual
工作得很好(也就是说,它可以访问未报告的字段),但我无法理解该函数的正反两面。这里有一个围棋区,展示了它的动作:。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
}
之所以可以这样做,是因为它可以访问包的未报告功能,在本例中是针对
valueInterface()
函数的,该函数使用
safe
参数,该参数通过
safe=true
方法拒绝访问未报告的字段值
reflect.DeepEqual()
将(可能)调用传递的
safe=false

您仍然可以这样做,但不能对未报告的字段使用
Value.Interface()
。相反,您必须使用特定于类型的方法,例如for
string
、for float、for int等。这些方法将返回值的副本(足以检查该值),但不允许您修改字段的值(如果
value.Interface(),则可能“部分”修改该值)
将起作用,字段类型将是指针类型)

如果字段恰好是接口类型,则可以使用获取接口值包含/包装的值

证明:

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())
}
输出(在上尝试):


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

此使用
不安全。指针的使用根据属性是有效的,并且运行
go vet
不会返回任何错误

如果结构不可寻址,此技巧将不起作用,但您可以创建如下可寻址副本:

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.
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.

根据客户的工作:


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

好的,但是你如何知道类型是什么,以便调用正确的方法?@UAvalos,例如,通过调用(字段的值)。这不会导致真正痛苦的开关阻塞吗?例如:case-Uint、case-Uint8、Uint16等…@UAvalos如果要处理所有类型,很遗憾,是的。如果您只想打印它的值,您可以简单地使用
value.String()
,它在它内部执行此操作。另外请注意,例如
Value.Int()
处理所有带符号整数类型(例如
int8
int16
int32
int64
Int
),因此这些类型可以帮助您“减轻”痛苦。Value.String()仅在其为字符串时返回值。否则它将返回值类型的字符串。非常好的技巧,谢谢。与
FieldByName
相结合,这是非常强大的,尽管很脏。什么是“struct is addressable”?@rakim:这意味着您可以获取它的地址,例如,它是一个局部变量或堆,而不是一个临时值,如函数的返回值。您可以在此处阅读更多内容: