继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Go 中关于方法的 receiver 的总结

UYOU
关注TA
已关注
手记 462
粉丝 86
获赞 459

关于这部分内容,在写代码时一直都是用指针类型的 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 mainimport (    "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 mainimport (    "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
链接:https://www.jianshu.com/p/da264d9b1d00

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP