为什么go中的receiver name不推荐使用this或者self

Go语言进阶学习

共 2536字,需浏览 6分钟

 ·

2022-06-06 20:59

点击上方“Go语言进阶学习”,进行关注

回复“Go语言”即可获赠从入门到进阶共10本电子书

香雾云鬟湿,清辉玉臂寒。


前言

在日常的开发中我们除了定义函数以外, 我们还会定义一些方法。这本来没有什么, 但是一些从PHP或者其他面向对象语言转GO的同学往往会把receiver name命名为thisselfme等。

笔者在实际项目开发中也遇到类似的同学, 屡次提醒却没有效果,于是决心写下这篇文章以便好好说服这些同学。

CR标准做法

首先我们来看一下GO推荐的标准命名Receiver Names,以下内容摘抄自https://github.com/golang/go/wiki/CodeReviewComments#receiver-names:

The name of a method's receiver should be a reflection of its identity;
often a one or two letter abbreviation of its type suffices (such as "c" or "cl" for "Client").
Don't use generic names such as "me", "this" or "self", identifiers typical of object-oriented languages that gives the method a special meaning.
In Go, the receiver of a method is just another parameter and therefore, should be named accordingly.
...

简单翻译总结有如下2点:

  1. 方法接受者名称应反映其身份, 并且不要使用methisself这些面向对象语言的典型标志符。

  2. 在go中方法接受者其实就是方法的另一个参数。

Receiver是方法的第一个参数!

上面的第二点, 可能不是很好理解,所以我们直接看下面的demo:

// T ...
type T int

// Println ...
func (t T) Println() {
fmt.Println("value: %v", t)
}

func main() {
t := T(1)
t.Println()

T.Println(t)

// receiver作为函数的第一个参数,这个时候发生值拷贝,所以方法内部的t变量是真实t变量的一个拷贝

// 这和this的含义是不相符的

}
// output:
value: 1
value: 1

通过上面的demo, 我们知道接受者可以直接作为第一个参数传递给方法的。而t.Println()应该就是Go中的一种语法糖了。

到这里可能有同学又要问了, 既然Go提供了这种语法糖,那我们这样命名有什么问题呢?笔者先不着急解释, 我们继续看下面的demo:

// Test ...
type Test struct {
A int
}

// SetA ...
func (t Test) SetA(a int) {
t.A = a
}

// SetA1 ...
func (t *Test) SetA1(a int) {
t.A = a
}

func main() {
t := Test{
A: 3,
}
fmt.Println("demo1:")
fmt.Println(t.A)
t.SetA(5)
fmt.Println(t.A)
t1 := Test{
A: 4,
}
fmt.Println("demo2:")
fmt.Println(t1.A)
(&t1).SetA1(6)
fmt.Println(t1.A)
}
// output:
demo1:
3
3
demo2:
4
6

看上面的demo我们知道, 当receiver不是指针时调用SetA其值根本没有改变。

因为Go中都是值传递,所以你如果对SetA的receiver的名称命名为thisself等,它就已经失去了本身的意义——“调用一个对象的方法就是向该对象传递一条消息”。而且对象本身的属性也并不一定会发生改变。

综上: 请各位读者在对receiver命名时不要再用thisself等具有特殊含义的名称啦。

Receiver是可以为nil的!!!

最近在研读h2_bundle.go的时候,发现了一段特殊的代码,顿时惊出一身冷汗,姑在本文补充一下,以防止自己和各位读者踩坑。

源代码截图如下: 

惊出我一身冷汗的正是图中标红的部分,receiver居然还要判断为nil!在我的潜意识里一直是这样认为的,receiver默认都是有值的,直接使用就行了。这简直颠覆我的认知,吓得我赶紧写了个demo验证一下:

type A struct {
v int
}

func (a *A) test() {
fmt.Println(a == nil)
}

func (a *A) testV() {
fmt.Println(a.v)
}


func main() {
var a *A
a.test()
a.testV()
}

上述输出如下:

a.test()能够正常输出,只有在处理变量结构体内部变量v才报出panic!!!还好本文前面已经介绍了Receiver是方法的第一个参数。正因为是第一个参数所以仅仅作为参数传递时即使是nil也能够正常调用函数,而在真正使用的地方报出panic。

鉴于receiver如此特殊,所以特意在本文完成之后补充后续内容以时刻提醒自己和各位读者。

本部分于20200827日晚补充。

欢迎关注公众号大家一同学习进步:

最后, 祝各位事业有成!

------------------- End -------------------

往期精彩文章推荐:

欢迎大家点赞,留言,转发,转载,感谢大家的相伴与支持

想加入Go学习群请在后台回复【入群

万水千山总是情,点个【在看】行不

浏览 75
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报