指针上定义的方法仍然可以使用值调用。

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

Method defined on pointer still callable with value

问题

《Effective Go》文档中提到了以下内容。

关于接收器(receiver)的指针与值的规则是,值方法可以在指针和值上调用,但指针方法只能在指针上调用。

http://tip.golang.org/doc/effective_go.html#pointers_vs_values

因此,如果我定义了如下的方法,它不会被值调用吗?

func (self *someStruct) Change(newNum int) {
    self.propertyOne = newNum
}

然而,以下代码似乎仍然有效。

structInstance := &someStruct{
    propertyOne: 41,
}
(*structInstance).Change(456)

为什么?

它是将值 (*structInstance) 转换回地址/指针以进行 Change 调用吗?

如何确保某个类型的某个实例不能调用指针上定义的方法(如 Change)?

Go Playground 演示

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

英文:

The "Effective Go" documentation says the following.

>The rule about pointers vs. values for receivers is that value methods can be invoked on pointers and values, but pointer methods can only be invoked on pointers.

http://tip.golang.org/doc/effective_go.html#pointers_vs_values

As such, if I define a method like the following, wouldn't it not be callable with a value?

func (self *someStruct) Change(newNum int) {
    self.propertyOne = newNum
}

However, the following still seems to work.

structInstance := &someStruct{
	propertyOne: 41,
}
(*structInstance).Change(456)

Why?

Is it converting the value (*structInstance) back into an address/pointer for the Change call?

How do I ensure that some instance of a type cannot call a method defined on the pointer (like Change)?

Go Playground Demo

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

答案1

得分: 3

当在指针接收器上定义一个函数时,Go会自动将值转换为指针,以便用于该函数。

type Cookies struct {
    Num int
}

func (c *Cookies) Buy(n int) {
    c.Num += n
}

func main() {
    c := Cookies{}
    c.Buy(10) // 等同于 (&c).Buy(10)
    fmt.Println(c)
}

我现在找不到定义的参考,但我知道它在官方文档的规范中的某个地方。

另外,请不要在Go中使用selfthis

//编辑

来自http://tip.golang.org/ref/spec#Calls:

如果方法集(类型的)x包含m并且参数列表可以分配给m的参数列表,则方法调用x.m()是有效的。如果x是可寻址的并且&x的方法集包含m,则x.m()是(&x).m()的简写形式

英文:

When a function is defined on a pointer receiver, Go will automatically convert a value to a pointer for that function.

type Cookies struct {
	Num int
}

func (c *Cookies) Buy(n int) {
	c.Num += n
}

func main() {
	c := Cookies{}
	c.Buy(10) //is equal to (&c).Buy(10)
	fmt.Println(c)
}

<strike>I can't find the reference to where that is defined right now, but I know it's in the official docs somewhere in the specs.</strike>

Also a side note, please don't use self or this in Go.

//edit

From http://tip.golang.org/ref/spec#Calls:

> A method call x.m() is valid if the method set of (the type of) x contains m and the argument list can be assigned to the parameter list of m. If x is addressable and &x's method set contains m, x.m() is shorthand for (&x).m()

答案2

得分: 1

语言规范中可以得知:

如果方法集(the method set)中包含方法 m,并且参数列表可以赋值给 m 的参数列表,那么方法调用 x.m() 是有效的。如果 x 是可寻址的,并且 &x 的方法集中包含 m,那么 x.m() 就是 (&x).m() 的简写形式。

在你的例子中,(*structInstance) 的方法集中没有 Change 方法,但它是可寻址的,并且 &(*structInstance) 的方法集中存在该方法,所以该调用被解释为 (&(*structInstance)).Change(456),或者更简单地说,structInstance.Change(456)

唯一防止这种行为的方法是在 someStruct 上也定义 Change 方法,可以将其定义为 panic。但这并不理想,因为它只会在运行时告诉你问题所在。更好的做法是重新组织程序结构,使得使用这种简写形式实际上并不重要。

英文:

From the language specification:

> A method call x.m() is valid if the method set of (the type of) x contains m and the argument list can be assigned to the parameter list of m. If x is addressable and &amp;x's method set contains m, x.m() is shorthand for (&amp;x).m()

In your example, there is no Change method in (*structInstance)'s method set, but it is addressable and the method exists in &amp;(*structInstance)'s method set, so the call is interpreted as (&amp;(*structInstance)).Change(456), or more simply structInstance.Change(456).

The only way to prevent this behaviour would be to also define the Change method on someStruct, perhaps making it panic. That's not ideal though, since it will only tell you about the problem at runtime. It would be less confusing to structure your program so that the use of this shorthand doesn't actually matter.

huangapple
  • 本文由 发表于 2014年8月13日 10:52:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/25277309.html
匿名

发表评论

匿名网友

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

确定