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

最初, 祝各位事业有成!

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理