乐趣区

关于golang:为什么go中的receiver-name不推荐使用this或者self

来自公众号:新世界杂货铺

前言

在日常的开发中咱们除了定义函数以外,咱们还会定义一些办法。这原本没有什么,然而一些从 PHP 或者其余面向对象语言转 GO 的同学往往会把 receiver name 命名为 this, self, me 等。

笔者在理论我的项目开发中也遇到相似的同学,多次揭示却没有成果,于是信心写下这篇文章以便好好压服这些同学。

CR 规范做法

首先咱们来看一下 GO 举荐的规范命名Receiver Names,以下内容摘抄自 https://github.com/golang/go/…:

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. 办法接受者名称应反映其身份,并且不要应用 me, this, self 这些面向对象语言的典型标志符。
  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 的名称命名为 this, self 等,它就曾经失去了自身的意义——“调用一个对象的办法就是向该对象传递一条音讯”。而且对象自身的属性也并不一定会产生扭转。

综上 : 请各位读者在对 receiver 命名时不要再用this, self 等具备非凡含意的名称啦。

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 日晚补充。

最初,祝各位事业有成!

退出移动版