使用反射通过字段名获取字段时,当检查IsZero时会引发恐慌。

huangapple go评论84阅读模式
英文:

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

huangapple
  • 本文由 发表于 2021年10月28日 16:07:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/69750397.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定