Go 如何访问未报告的结构字段
是否有方法使用reflect访问Go 1.8中未报告的字段? 这似乎不再有效: 请注意,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.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()
。相反,您必须使用特定于类型的方法,例如forstring
、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:这意味着您可以获取它的地址,例如,它是一个局部变量或堆,而不是一个临时值,如函数的返回值。您可以在此处阅读更多内容: