确定结构体接口类型的字段是否已设置

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

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()将导致空指针解引用的恐慌。但是反射不允许您测试IsNilIsZero也不会返回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 &amp;a{} }

type b struct {}
func (*b) Bar() {}
func NewB() *b { return &amp;b{} }

And code that creates an instance of container but does not explicitly set all fields:

c := &amp;container{}
c.FieldA = NewA()
// c.FieldB = NewB()  // &lt;-- 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 &lt; 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 (
	&quot;fmt&quot;
	&quot;reflect&quot;
)

func validateContainer(c *container) {
	tC := reflect.TypeOf(*c)
	vC := reflect.ValueOf(*c)
	for i := 0; i &lt; vC.NumField(); i++ {
		if f := vC.Field(i); f.Kind() == reflect.Interface {
			fmt.Printf(&quot;%v: IsNil: %v, isZero: %v, IsValid: %v\n&quot;,
				tC.Field(i).Name,
				f.IsNil(),
				f.IsZero(),
				f.IsValid(),
			)
		}
	}

	// tC.Field(i) returns a reflect.StructField that describes the field.
	// It&#39;s not the field itself.
	fmt.Printf(&quot;%#v\n&quot;, 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 &amp;a{} }

func main() {
	c := &amp;container{}
	c.FieldA = NewA()
	validateContainer(c)
}

Output:

FieldA: IsNil: false, isZero: false, IsValid: true
FieldB: IsNil: true, isZero: true, IsValid: true
reflect.StructField{Name:&quot;FieldA&quot;, PkgPath:&quot;&quot;, Type:(*reflect.rtype)(0x48de20), Tag:&quot;&quot;, Offset:0x0, Index:[]int{0}, Anonymous:false}

huangapple
  • 本文由 发表于 2023年3月25日 08:38:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/75839041.html
匿名

发表评论

匿名网友

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

确定