英文:
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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论