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