在检查 IsZero 时使用 reflect panics 按名称获取字段

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 上的文档,如果未找到字段,此函数应 return 为零值。但是,下一行因错误而恐慌:

panic: reflect: call of reflect.Value.IsZero on zero Value

goroutine 268 [running]:
reflect.Value.IsZero({0x0, 0x0, 0x112a974})
        reflect/value.go:1475 +0x27f

根据 this GitHub issue,只有当值包含 nil(即无类型)时才会发生这种情况,应该使用 IsValid 代替。为什么会这样?

Value.IsZero() 报告包装值是否是其类型的零值。这与 reflect.Value 本身为零(reflect.Value 的零值是一个结构)不同。

另请注意,代码中的 t 不是结构值,它是指向结构的指针。使用 Value.Elem() 导航到包装的结构值(或者不要从指针开始)。

如果该字段不存在,Value.FieldByName() returns the zero value of 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