

Go错误集锦 | 聊聊方法接收者的值类型和指针类型之间的区别
source link: https://studygolang.com/articles/35447
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Go错误集锦 | 聊聊方法接收者的值类型和指针类型之间的区别
yudotyang · 5天之前 · 138 次点击 · 预计阅读时间 3 分钟 · 大约8小时之前 开始浏览大家好,我是『Go学堂』的渔夫子。今天跟大家聊聊方法接收者类型的话题。
原文链接:https://mp.weixin.qq.com/s/Av3DrzDXa2cjjBbtkj6wuw
我们知道,在Go中定义了结构体后,可以给该结构体定义方法。如下:
type customer struct {
balance string
}
func (c customer) SetBalance(v float64) {
s.balance = v
}
func (c *customer) UpdateBalance(v float64) {
c.balance = v
}
这里的c就是方法的接收者。该接收者的类型可以是一个对象的值,也可以是一个对象的指针。
那么接收者的值类型和指针类型他们之间有什么区别?我们在定义方法时,接收者是该选择使用值类型还是选择使用指针类型呢?
01 方法接收者是值类型
在Go中,大家都听过的一切都是拷贝。所以,当方法的接收者是一个值类型时,实际上是对原来对象的一个拷贝,然后让该对象的拷贝再来调用对应的方法。在方法中对接收者的任何改变,都不会影响原对象。
下面的通过一段具体的示例来说明。
type customer struct {
balance float64
}
func (c customer) add(v float64) {
c.balance += v
}
func main() {
c := customer{balance: 100.}
c.add(50.)
fmt.Printf("balance: %.2f\n", c.balance)
}
因为在add方法中,接收者是值类型,在执行c.add(50.)函数时,实际上是对c进行了拷贝,然后改变了新拷贝的对象的balance。所以,最终c.balance的结果没有任何改变,依然是100。如图所示:
02 方法接收者是指针类型
如果接收者的类型是指针,那么,我们传递给方法的是原对象的地址,依然是值拷贝,这里的值是地址值,而非是原对象的拷贝。这时,在方法中对接收者的任何改变,都会作用到原对象上。
依然是上面的示例,我们将接收者类型更改成指针。
type customer struct {
balance float64
}
func (c *customer) add(v float64) {
c.balance += v
}
func main() {
c := customer{balance: 100.}
c.add(50.)
fmt.Printf("balance: %.2f\n", c.balance)
}
因为接收者是指针类型,所以,对balance的更改实际上是对原对象的更改,最终结果会输出150。如图所示:
03 接收者的类型该如何选择
在定义结构体方法时,接收者类型是使用值类型还是指针类型呢?下面我们列出一些常见的选择依据来帮助我们选择使用哪种类型。
接收者必须是指针类型的场景:
- 如果方法需要对接收者进行改变时,则必须是指针类型。这条规则同样适用于切片类型。如果接收者类型是一个切片,同时在方法中我们想在切片中增加元素时,如下:
type slice []int
func (s *slice) add(element int) {
*s = append(*s, element)
}
- 如果接收者包含有不能拷贝的字段时,则必须是指针类型。例如sync包中的类型字段是不能被拷贝的。
接收者建议使用指针类型的场景:
- 如果接收者是一个很大的对象时,建议优先使用指针类型。使用指针类型能够进行快速拷贝,可以提高调用方法的效率。那么,多大的才算是大对象呢,这没有标准,一般建议是在实际项目中通过基准测试来决定。
接收者必须是值类型的场景:
- 当必须保持接收者的不变性时,即在函数中不能改变原有对象时。
- 当接收者是map、function或channel类型时。否则,会导致编译错误。
接收者建议使用值类型的场景:
- 当接收者是一个不被改变的切片类型时。
- 当接收者的类型是一个基础的类型时。Go的基础类型包括Numbers、strings、boolean。
- 当接收者是一个小对象同时不符合使用指针的条件时。
04 一个示例
下面我们看一个稍微复杂点示例。在该示例中,customer结构体中包含了一个指针类型的字段。示例如下:
type customer struct {
card *card
}
type card struct {
balance float64
}
func (c customer) add(operation float64) {
c.card.balance += operation
}
func main() {
c := customer{card: &card{
balance: 100,
}}
c.add(50.)
fmt.Printf("balance: %.2f\n", c.card.balance)
}
在该示例中,balance是card结构体中的字段,而customer中通过指针引入了card。同时,方法的接收者类型我们依然使用的是值类型,但最终结果依然会改变原对象中balance的值。
有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:701969077
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK