Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Go 中关于方法的 receiver 的总结 #11

Open
LLLeon opened this issue Aug 16, 2018 · 0 comments
Open

Go 中关于方法的 receiver 的总结 #11

LLLeon opened this issue Aug 16, 2018 · 0 comments
Labels
Notes_Go Notes on learning Golang.

Comments

@LLLeon
Copy link
Owner

LLLeon commented Aug 16, 2018

关于这部分内容,在写代码时一直都是用指针类型的 receiver,但没有系统整理过规则,这里进行总结。

首先是官方 FAQ 中说的那三条:

  • 第一条也是最重要的一条,方法是否要修改 receiver?
  • 其次是效率的考虑,如果 receiver 非常大,比如说一个大 struct,使用指针将非常合适。
  • 接下来是一致性,如果该类型的某些方法必须使用指针 receiver,剩下的也要使用指针。不论使用什么类型的 receiver,方法集要一致。

还有一些其它的规则:

  • 实例和实例指针可以调用值类型和指针类型 receiver 的方法。
  • 如果通过 method express 方式,struct 值只能调用值类型 receiver 的方法,而 struct 指针是能调用值类型和指针类型 receiver 的方法的。
  • 如果 receiver 是 mapfuncchan,不要使用指针。
  • 如果 receiver 是 slice,并且方法不会重新分配 slice,不要使用指针。
  • 如果 receiver 是包含 sync.Mutex 或其它类似的同步字段的结构体,receiver 必须是指针,以避免复制。
  • 如果 receiver 是大 structarray,receiver 用指针效率会更高。那么,多大是大?假设要把它的所有元素作为参数传递给方法,如果这样会感觉太大,那对 receiver 来说也就太大了。
  • 如果 receiver 是 structarrayslice,并且它的任何元素都是可能发生改变的内容的指针,最好使用指针类型的 receiver,这会使代码可读性更高。
  • 如果 receiver 是一个本来就是值类型的小 arraystruct,没有可变字段,没有指针,或只是一个简单的基础类型,如 intstring,使用值类型的 receiver 更合适。
  • 值类型的 receiver 可以减少可以生成的垃圾量,如果将值传递给值方法,可以使用栈上的副本而不是在堆上进行分配。编译器会尝试避免这种分配,但不会总成功。不要为此原因却不事先分析而选择值类型的 receiver。
  • 最后,如有疑问,请使用指针类型的 receiver。

下面看两个比较容易搞混的例子:

package main

import (
	"fmt"
)

type Ball struct {
	Name string
}

func (b *Ball) Ping() {
	fmt.Println("ping")
}

func (b Ball) Pong() {
	fmt.Println("pong")
}

func main() {
	v := Ball{}
	p := &Ball{}

	v.Ping()
	v.Pong()

	p.Ping()
	p.Pong()
}

运行结果是都可以正常执行:

❯ go run test.go
ping
pong
ping
pong

也就是说,struct 的实例和实例指针都可以调用值类型和指针类型 receiver 的方法。

再看这段代码,这里是通过 method expression 的方式调用方法:

package main

import (
	"fmt"
)

type Ball struct {
	Name string
}

func (b *Ball) Ping() {
	fmt.Println("ping")
}

func (b Ball) Pong() {
	fmt.Println("pong")
}

func main() {
	v := Ball{}
	
	Ball.Ping(&v)
	Ball.Pong(v)
}

这次的执行结果呢?

❯ go run test.go
# command-line-arguments
./t.go:23:6: invalid method expression Ball.Ping (needs pointer receiver: (*Ball).Ping)
./t.go:23:6: Ball.Ping undefined (type Ball has no method Ping)

可以看到,通过 method expression 的方式,struct 值只能调用值类型 receiver 的方法。

再看 struct 指针调用方法:

func main() {
	p := &Ball{}

	(*Ball).Ping(p)
	(*Ball).Pong(p)
}

执行结果:

❯ go run test.go
ping
pong

即 struct 指针是能调用值类型和指针类型 receiver 的方法的。

但在写代码时,不建议使用 method expression 这种方式来调用方法。不过应该也没有人会用这种方式的...吧?

@LLLeon LLLeon added the Notes_Go Notes on learning Golang. label Aug 16, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Notes_Go Notes on learning Golang.
Projects
None yet
Development

No branches or pull requests

1 participant