英文:
Getting field by name using reflect panics when checking IsZero
问题
我有一段反射代码,试图通过名称获取结构体上的字段,然后检查字段是否存在:
type test struct {
A bool
B bool
}
t := new(test)
metaValue := reflect.ValueOf(t).Elem()
field := metaValue.FieldByName(name)
if field.IsZero() {
glog.Errorf("Field %s was not on the struct", inner)
}
根据FieldByName
的文档,如果没有找到字段,该函数应返回零值。然而,紧接着的一行代码却引发了错误:
panic: reflect: call of reflect.Value.IsZero on zero Value
goroutine 268 [running]:
reflect.Value.IsZero({0x0, 0x0, 0x112a974})
reflect/value.go:1475 +0x27f
根据这个 GitHub 问题,只有当 Value 包含 nil(即没有类型)时才会发生这种情况,应该使用 IsValid
来代替。为什么会发生这种情况?
英文:
I have a piece of reflection code that attempts to get the field on a struct by name and then checks if the field exists:
type test struct {
A bool
B bool
}
t := new(test)
metaValue := reflect.ValueOf(t).Elem()
field := metaValue.FieldByName(name)
if field.IsZero() {
glog.Errorf("Field %s was not on the struct", inner)
}
According to the documentation on FieldByName
, this function should return a zero value if no field was found. However, the very next line panics with the error:
panic: reflect: call of reflect.Value.IsZero on zero Value
goroutine 268 [running]:
reflect.Value.IsZero({0x0, 0x0, 0x112a974})
reflect/value.go:1475 +0x27f
According to this GitHub issue, this should only happen if the Value contains nil (i.e. no type) and IsValid
should be used instead. Why is this happening?
答案1
得分: 1
Value.IsZero()
报告封装值是否为其类型的零值。这与reflect.Value
本身是否为零值(reflect.Value
的零值是一个结构体)不同。
还要注意,你代码中的t
不是一个结构体值,而是一个指向结构体的指针。使用Value.Elem()
导航到封装的结构体值(或者不要从指针开始)。
如果字段不存在,Value.FieldByName()
返回reflect.Value
的零值,而不是持有某种类型的零值的非零reflect.Value
;如果找不到字段,则没有类型信息。
因此,要检查字段是否不存在,请将其与reflect.Value{}
进行比较,以检查reflect.Value
本身是否为零:
if field == (reflect.Value{}) {
log.Printf("Field %s was not on the struct", name)
}
进行测试:
type test struct {
A bool
B bool
x bool
}
v := new(test)
metaValue := reflect.ValueOf(v).Elem()
for _, name := range []string{"A", "x", "y"} {
field := metaValue.FieldByName(name)
if field == (reflect.Value{}) {
log.Printf("Field %s was not on the struct", name)
}
}
这将输出(在Go Playground上尝试):
2009/11/10 23:00:00 Field y was not on the struct
英文:
Value.IsZero()
reports whether the wrapped value is the zero value of its type. This is not the same as the reflect.Value
itself being zero (zero value of reflect.Value
which is a struct).
Also note that t
in your code is not a struct value, its a pointer to struct. Use Value.Elem()
to nagivate to the wrapped struct value (or don't start from a pointer).
If the field does not exist, Value.FieldByName()
returns the zero value of reflect.Value
, not a non-zero reflect.Value
holding the zero value of some type; there is no type info if a field is not found.
So to check if the field does not exist, check if the reflect.Value
itself is zero, by comparing it to reflect.Value{}
:
if field == (reflect.Value{}) {
log.Printf("Field %s was not on the struct", name)
}
Testing it:
type test struct {
A bool
B bool
x bool
}
v := new(test)
metaValue := reflect.ValueOf(v).Elem()
for _, name := range []string{"A", "x", "y"} {
field := metaValue.FieldByName(name)
if field == (reflect.Value{}) {
log.Printf("Field %s was not on the struct", name)
}
}
This will output (try it on the Go Playground):
2009/11/10 23:00:00 Field y was not on the struct
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论