英文:
Determine whether field of interface-type of struct is set
问题
给定一个结构体,其字段是接口类型:
type A interface { Foo() }
type B interface { Bar() }
type container struct {
FieldA A
FieldB B
...
}
以及实现这些接口的结构体:
type a struct {}
func (*a) Foo() {}
func NewA() *a { return &a{} }
type b struct {}
func (*b) Bar() {}
func NewB() *b { return &b{} }
还有创建container实例的代码,但没有显式设置所有字段:
c := &container{}
c.FieldA = NewA()
// c.FieldB = NewB() // <--- FieldB没有显式设置
使用反射,如何检测未显式设置的字段?
在运行时检查FieldB的值时,VSCode会愉快地报告该值为nil。同样,尝试调用c.FieldB.Bar()将导致空指针解引用的恐慌。但是反射不允许您测试IsNil,IsZero也不会返回true:
func validateContainer(c *container) {
tC := reflect.TypeOf(*c)
for i := 0; i < tC.NumField(); i++ {
reflect.ValueOf(tC.Field(i)).IsNil() // 引发恐慌
reflect.ValueOf(tC.Field(i)).IsZero() // 总是返回false
reflect.ValueOf(tC.Field(i)).IsValid() // 总是返回true
}
}
英文:
Given a struct who's fields are of interface types:
type A interface { Foo() }
type B interface { Bar() }
type container struct {
FieldA A
FieldB B
...
}
And structs that implement those interfaces:
type a struct {}
func (*a) Foo() {}
func NewA() *a { return &a{} }
type b struct {}
func (*b) Bar() {}
func NewB() *b { return &b{} }
And code that creates an instance of container but does not explicitly set all fields:
c := &container{}
c.FieldA = NewA()
// c.FieldB = NewB() // <-- FieldB not explicitly set
Using reflection, how can I detect the fields that were not explicitly set?
Inspecting the value of FieldB at runtime, VSCode happily reports the value to be nil. Likewise, attempting to invoke c.FieldB.Bar() will panic with nil pointer dereference. But reflection wont allow you test for IsNil, nor does IsZero return true:
func validateContainer(c *container) {
tC := reflect.TypeOf(*c)
for i := 0; i < tC.NumField(); i++ {
reflect.ValueOf(tC.Field(i)).IsNil() // panics
reflect.ValueOf(tC.Field(i)).IsZero() // always false
reflect.ValueOf(tC.Field(i)).IsValid() // always true
}
}
答案1
得分: 2
你应该检查reflect.ValueOf(*c)的字段,而不是reflect.TypeOf(*c)的字段。
package main
import (
"fmt"
"reflect"
)
func validateContainer(c *container) {
tC := reflect.TypeOf(*c)
vC := reflect.ValueOf(*c)
for i := 0; i < vC.NumField(); i++ {
if f := vC.Field(i); f.Kind() == reflect.Interface {
fmt.Printf("%v: IsNil: %v, isZero: %v, IsValid: %v\n",
tC.Field(i).Name,
f.IsNil(),
f.IsZero(),
f.IsValid(),
)
}
}
// tC.Field(i)返回描述字段的reflect.StructField,而不是字段本身。
fmt.Printf("%#v\n", reflect.ValueOf(tC.Field(0)))
}
type A interface{ Foo() }
type B interface{ Bar() }
type container struct {
FieldA A
FieldB B
}
type a struct{}
func (*a) Foo() {}
func NewA() *a { return &a{} }
func main() {
c := &container{}
c.FieldA = NewA()
validateContainer(c)
}
输出:
FieldA: IsNil: false, isZero: false, IsValid: true
FieldB: IsNil: true, isZero: true, IsValid: true
reflect.StructField{Name:"FieldA", PkgPath:"", Type:(*reflect.rtype)(0x48de20), Tag:"", Offset:0x0, Index:[]int{0}, Anonymous:false}
英文:
You should check the fields of reflect.ValueOf(*c) instead of reflect.TypeOf(*c).
package main
import (
"fmt"
"reflect"
)
func validateContainer(c *container) {
tC := reflect.TypeOf(*c)
vC := reflect.ValueOf(*c)
for i := 0; i < vC.NumField(); i++ {
if f := vC.Field(i); f.Kind() == reflect.Interface {
fmt.Printf("%v: IsNil: %v, isZero: %v, IsValid: %v\n",
tC.Field(i).Name,
f.IsNil(),
f.IsZero(),
f.IsValid(),
)
}
}
// tC.Field(i) returns a reflect.StructField that describes the field.
// It's not the field itself.
fmt.Printf("%#v\n", reflect.ValueOf(tC.Field(0)))
}
type A interface{ Foo() }
type B interface{ Bar() }
type container struct {
FieldA A
FieldB B
}
type a struct{}
func (*a) Foo() {}
func NewA() *a { return &a{} }
func main() {
c := &container{}
c.FieldA = NewA()
validateContainer(c)
}
Output:
FieldA: IsNil: false, isZero: false, IsValid: true
FieldB: IsNil: true, isZero: true, IsValid: true
reflect.StructField{Name:"FieldA", PkgPath:"", Type:(*reflect.rtype)(0x48de20), Tag:"", Offset:0x0, Index:[]int{0}, Anonymous:false}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论