golang – 指针的特殊性

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

golang - Pointer idiosyncrasies

问题

需要帮助理解为什么这段代码会出错。PrintFoo可以使用指针或值来调用。为什么NumField不行?

  1. type A struct {
  2. foo string
  3. }
  4. func (a *A) PrintFoo(){
  5. fmt.Println("Foo value is " + a.foo)
  6. }
  7. func main() {
  8. a := &A{foo: "afoo"}
  9. (*a).PrintFoo() //可以正常工作
  10. a.PrintFoo() //可以正常工作
  11. reflect.TypeOf(*a).NumField() //可以正常工作 - Type = main.A
  12. reflect.TypeOf(a).NumField() //出错!- Type = *main.A
  13. }

链接:http://play.golang.org/p/Kw16ReujRx

英文:

Need help understanding why this breaks. PrintFoo can be called using either pointer or value. Why not NumField?

http://play.golang.org/p/Kw16ReujRx

  1. type A struct {
  2. foo string
  3. }
  4. func (a *A) PrintFoo(){
  5. fmt.Println("Foo value is " + a.foo)
  6. }
  7. func main() {
  8. a := &A{foo: "afoo"}
  9. (*a).PrintFoo() //Works - no problem
  10. a.PrintFoo() //Works - no problem
  11. reflect.TypeOf(*a).NumField() //Works - no problem - Type = main.A
  12. reflect.TypeOf(a).NumField() //BREAKS! - Type = *main.A
  13. }

答案1

得分: 5

根据文档

// NumField 返回结构体 v 中的字段数量。
// 如果 v 的类型不是 Struct,会引发 panic。
func (v Value) NumField() int

你正在对一个指针进行调用,你需要对一个结构体进行调用,例如示例

fmt.Println(reflect.Indirect(reflect.ValueOf(a)).NumField())
fmt.Println(reflect.Indirect(reflect.ValueOf(*a)).NumField())

当你不确定你的值是指针还是其他类型时,可以使用reflect.Indirect

Indirect 返回 v 指向的值。如果 v 是一个空指针,Indirect 返回一个零值。如果 v 不是指针,Indirect 返回 v。

//编辑:

NumField 是在 Value 上调用的,而不是你的实际对象,例如如果你这样做:

func main() {
a := &A{foo: "afoo"}
fmt.Printf("%#v\n", reflect.TypeOf(*a))
fmt.Printf("%#v\n", reflect.TypeOf(a))
}

你会得到:

//*a
&reflect.rtype{size:0x8, ...... ptrToThis:(*reflect.rtype)(0xec320)}
//a
&reflect.rtype{size:0x4, ...... ptrToThis:(*reflect.rtype)(nil)}

可以看出,它们是完全不同的。

  • 第一个持有指针的信息,因此 ptrToThis 指向实际的结构体。
  • 第二个持有结构体本身的信息。
英文:

From the documentation :

  1. // NumField returns the number of fields in the struct v.
  2. // It panics if v's Kind is not Struct.
  3. func (v Value) NumField() int

You are calling it on a pointer, you have to call it on a struct instead, for example :

  1. fmt.Println(reflect.Indirect(reflect.ValueOf(a)).NumField())
  2. fmt.Println(reflect.Indirect(reflect.ValueOf(*a)).NumField())

When you're not sure if your value is a pointer or not, use reflect.Indirect:

> Indirect returns the value that v points to. If v is a nil pointer,
> Indirect returns a zero Value. If v is not a pointer, Indirect returns
> v.

//edit:

NumField gets called on Value, not your actual object, for example of you do:

  1. func main() {
  2. a := &A{foo: "afoo"}
  3. fmt.Printf("%#v\n", reflect.TypeOf(*a))
  4. fmt.Printf("%#v\n", reflect.TypeOf(a))
  5. }

You will get :

  1. //*a
  2. &reflect.rtype{size:0x8, ...... ptrToThis:(*reflect.rtype)(0xec320)}
  3. //a
  4. &reflect.rtype{size:0x4, ...... ptrToThis:(*reflect.rtype)(nil)}

As you can tell, it's a completely different beast.

  • The first one holds information about the pointer, hence ptrToThis points to the actual struct.
  • The second holds info about the struct itself.

huangapple
  • 本文由 发表于 2014年6月21日 06:26:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/24336537.html
匿名

发表评论

匿名网友

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

确定