尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Go方法:值接收者vs指针接收者

Go方法:值接收者vs指针接收者 TL;DR 核心要点速览Go编译型语言,执行速度比Python快10倍Goroutine初始栈2KB,比线程轻100倍Channel是Go并发通信的核心原语GMP调度器自动管理goroutine调度Go标准库覆盖HTTP/JSON/加密等常用场景本篇是Go入门模块,建议按顺序学习摘要Go方法详解,值接收者vs指针接收者的区别和选择,含方法集、嵌入字段和接口实现第9篇 Go方法值接收者vs指针接收者摘要Go语言方法通过接收者绑定到类型上本文深入对比值接收者与指针接收者的语义差异、性能影响和接口实现规则帮你彻底搞清楚什么时候该用指针接收者避免修改无效和内存拷贝的性能坑。有一次我给一个结构体写了个SetName方法调用之后名字死活没改过来。调试了半天发现方法定义的时候用了值接收者结构体的修改根本没有生效。当时我还骂了一句这设计也太反直觉了。后来搞明白了值接收者和指针接收者的区别是Go方法体系里最核心的概念之一。选错了要么修改不生效要么白白多拷贝一次内存。今天我把这两种接收者的区别彻底讲透。一、方法的基本语法Go没有class方法就是给类型绑定一个函数。绑定用的就是接收者参数写在func关键字后面。packagemainimportfmt// Rectangle 矩形结构体typeRectanglestruct{Widthfloat64// 宽度Heightfloat64// 高度}// Area 计算面积// 这里的r就是接收者相当于this或self// 值接收者r是原结构体的副本func(r Rectangle)Area()float64{returnr.Width*r.Height}// Perimeter 计算周长func(r Rectangle)Perimeter()float64{return2*(r.Widthr.Height)}funcmain(){rect:Rectangle{Width:10,Height:5}// 调用方法和调用普通函数一样的语法fmt.Println(面积,rect.Area())// 输出 50fmt.Println(周长,rect.Perimeter())// 输出 30}接收者跟Java里的this或者Python里的self类似都是指向当前对象。区别在于Go的接收者可以是值也可以是指针这两种选择会带来完全不同的行为。二、值接收者 vs 指针接收者这是今天的核心。先看个对比代码。packagemainimportfmt// Counter 计数器结构体typeCounterstruct{Countint}// Increment 值接收者版本// 这里的c是Counter的副本修改不会影响原对象func(c Counter)Increment(){c.Count// 这行修改的是副本原对象不受影响}// IncrementBy 指针接收者版本// 这里的c是指向原对象的指针修改直接生效func(c*Counter)IncrementBy(nint){c.Countn// 通过指针修改原对象}funcmain(){c1:Counter{Count:0}c1.Increment()// 调用值接收者方法fmt.Println(值接收者后,c1.Count)// 输出 0没变c2:Counter{Count:0}c2.IncrementBy(5)// 调用指针接收者方法fmt.Println(指针接收者后,c2.Count)// 输出 5变了// Go会自动取地址所以c2.IncrementBy(5)等价于(c2).IncrementBy(5)// 这就是为什么写法看起来没区别但行为完全不同}这就是我开头踩坑的原因。值接收者拿到的是结构体的副本你在方法里改的只是副本函数一返回副本就没了原对象纹丝不动。指针接收者拿到的是指向原对象的指针改了就是真改了。三、什么时候用指针接收者选择标准其实很简单记住几条规则就行。packagemainimportfmt// BigStruct 一个大结构体字段很多typeBigStructstruct{Data[1000]int// 大数组拷贝成本很高Namestring}// Sum 用值接收者因为只读不改// 但如果结构体很大拷贝一次1000个int代价很高func(b BigStruct)Sum()int{total:0for_,v:rangeb.Data{totalv}returntotal}// SetData 用指针接收者因为要修改字段func(b*BigStruct)SetData(index,valueint){ifindex0indexlen(b.Data){b.Data[index]value}}// Reset 指针接收者修改字段// 同时避免大结构体拷贝func(b*BigStruct)Reset(){fori:rangeb.Data{b.Data[i]0}b.Name}funcmain(){big:BigStruct{Name:test}big.SetData(0,42)fmt.Println(第一个元素,big.Data[0])// 输出 42fmt.Println(总和,big.Sum())// 输出 42big.Reset()fmt.Println(重置后名字,big.Name)// 输出空字符串}总结一下选择规则。第一方法需要修改接收者的状态就用指针接收者。第二结构体很大用指针接收者避免每次调用都拷贝一次。第三一致性原则如果一个类型有任何一个方法用了指针接收者那所有方法都应该用指针接收者保持一致。小结构体比如几个int或float只读的方法用值接收者就行拷贝成本可以忽略。大结构体或者需要修改的方法一律指针接收者。四、独家踩坑说个我遇到过的问题。我在一个项目里用值接收者实现了几个方法然后想把这些方法抽成一个接口。结果编译死活通不过提示类型没有实现接口。代码大概是这么个情况。packagemainimportfmt// Modifier 接口要求实现SetName方法typeModifierinterface{SetName(namestring)}// Product 产品结构体typeProductstruct{Namestring}// SetName 用了值接收者// 这意味着Product类型实现了SetName// 但*Product类型的修改不生效func(p Product)SetName(namestring){p.Namename// 值接收者改了也白改}// 正确的写法应该是指针接收者// func (p *Product) SetName(name string) {// p.Name name// }funcmain(){p:Product{}// 值接收者的问题改了不生效p.SetName(手机)fmt.Println(名字,p.Name)// 输出空字符串没改成功// 如果接口方法要求修改状态// 必须用指针接收者才能正确实现接口// 用值接收者时 *Product 也能调用这个方法// 但通过接口调用时修改不生效}这里涉及一个Go的规则。值接收者的方法集只包含值接收者的方法指针接收者的方法集同时包含值接收者和指针接收者的方法。反过来说如果你用值接收者定义方法那么值类型和指针类型都能调用这个方法。但如果你用指针接收者定义方法只有指针类型实现了这个方法值类型没有实现。当时我的SetName用了值接收者虽然指针类型也能调用这个方法但修改不生效。所以涉及到修改状态的接口方法必须用指针接收者。这事儿排查了一个多小时后来团队里定了个规范凡是Set开头的方法一律用指针接收者。五、对比分析跟Java和Python的方法对比一下。Java的方法默认就是按引用操作对象this指向当前对象没有值接收者的概念。你没法选择传值还是传引用基本类型按值对象按引用固定死的。Go给了你选择权值接收者还是指针接收者自己决定灵活但也容易出错。Python的self跟Go的指针接收者类似都是引用当前对象。Python没有值传递对象的概念所有对象都是引用传递。性能方面Go的值接收者会拷贝整个结构体。对于小结构体无所谓但如果结构体里有大的数组或slice每次方法调用都拷贝一次性能会很差。Java没有这个问题因为对象都是堆上的引用但JVM的GC压力会更大。六、总结值接收者拿到的是副本修改不生效适合小结构体的只读方法。指针接收者拿到的是指针修改直接生效适合需要修改状态或结构体较大的场景。一致性很重要一个类型的方法接收者风格要统一。下一篇我们聊Go的接口看看Go是怎么用隐式实现做类型系统的。相关推荐Go控制结构if、for、switch的那些坑Go函数参数、返回值、匿名函数与闭包Go数组与切片底层原理与性能陷阱常见问题 FAQQ: Go语言好学吗有编程基础需要多久A: Go语法仅25个关键字,有编程基础1周可入门,3个月能写生产级后端服务。Q: Go和Python/Java哪个好A: Go在云原生和微服务方向需求旺盛,执行速度比Python快10倍,比Java更轻量。Q: Go语言薪资水平怎么样A: Go后端开发薪资15-50K,中级20-35K,高级30-45K,一线城市更高。Q: 学Go需要什么基础A: 了解基本编程概念即可,Go语法简单,专栏从零开始讲解。Q: Go的泛型成熟了吗A: Go 1.18引入泛型,1.22已成熟可用,专栏有专门章节讲解。为什么订阅本专栏对比维度本专栏(160篇)免费资源培训班内容深度源码级分析生产踩坑入门级示例系统级讲解实战项目10完整可运行项目片段代码2-3个项目踩坑经验每篇1个独家生产事故几乎没有少量对比分析每篇1个三方对比表格无偶尔提及价格99元(首发价)0元5000-20000元更新频率已完结160篇不定期一次性答疑支持评论区24h内回复无有限时99元,30秒完成订阅,160篇Go语言云原生实战内容全部解锁,今天就能开始系统学习
返回列表