Go错误集锦 | 聊聊方法接收者的值类型和指针类型之间的区别

yudotyang · · 622 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

大家好,我是『Go学堂』的渔夫子。今天跟大家聊聊方法接收者类型的话题。 原文链接:https://mp.weixin.qq.com/s/Av3DrzDXa2cjjBbtkj6wuw 我们知道,在Go中定义了结构体后,可以给该结构体定义方法。如下: ```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中,大家都听过的一切都是拷贝。所以,当方法的接收者是一个值类型时,实际上是对原来对象的一个拷贝,然后让该对象的拷贝再来调用对应的方法。在方法中对接收者的任何改变,都不会影响原对象。 ​ 下面的通过一段具体的示例来说明。 ```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。如图所示: ![01-函数接收者-值类型.png](https://cdn.gocn.vip/forum-user-images/20220214/4912c94f04ec45cbaed225269b896af8.jpg) ​ **02 方法接收者是指针类型** 如果接收者的类型是指针,那么,我们传递给方法的是原对象的地址,依然是值拷贝,这里的值是地址值,而非是原对象的拷贝。这时,在方法中对接收者的任何改变,都会作用到原对象上。 ​ 依然是上面的示例,我们将接收者类型更改成指针。 ​ ```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) } ``` 因为接收者是指针类型,所以,对balance的更改实际上是对原对象的更改,最终结果会输出150。如图所示: ![02-函数接收者-指针类型.png](https://cdn.gocn.vip/forum-user-images/20220214/56905b82d5cb49bbb747369a70c158a2.jpg) ​ **03 接收者的类型该如何选择** 在定义结构体方法时,接收者类型是使用值类型还是指针类型呢?下面我们列出一些常见的选择依据来帮助我们选择使用哪种类型。 ​ 接收者必须是指针类型的场景: - 如果方法需要对接收者进行改变时,则必须是指针类型。这条规则同样适用于切片类型。如果接收者类型是一个切片,同时在方法中我们想在切片中增加元素时,如下: ```go type slice []int func (s *slice) add(element int) { *s = append(*s, element) } ``` - 如果接收者包含有不能拷贝的字段时,则必须是指针类型。例如sync包中的类型字段是不能被拷贝的。 接收者建议使用指针类型的场景: - 如果接收者是一个很大的对象时,建议优先使用指针类型。使用指针类型能够进行快速拷贝,可以提高调用方法的效率。那么,多大的才算是大对象呢,这没有标准,一般建议是在实际项目中通过基准测试来决定。 接收者必须是值类型的场景: - 当必须保持接收者的不变性时,即在函数中不能改变原有对象时。 - 当接收者是map、function或channel类型时。否则,会导致编译错误。 接收者建议使用值类型的场景: - 当接收者是一个不被改变的切片类型时。 - 当接收者的类型是一个基础的类型时。Go的基础类型包括Numbers、strings、boolean。 - 当接收者是一个小对象同时不符合使用指针的条件时。 **04 一个示例** 下面我们看一个稍微复杂点示例。在该示例中,customer结构体中包含了一个指针类型的字段。示例如下: ```go 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群:692541889

622 次点击  ∙  1 赞  
加入收藏 微博
1 回复  |  直到 2022-02-15 09:34:39
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传