在Go中命名接收器变量'self'误导或良好实践?

mil*_*onb 37 self go

我在Go上看到过相当多的博客和视频,据我所知,在编写方法时,没有一个作者使用"self"或"this"作为receiver变量.然而,有关堆栈溢出的问题似乎有很多问题,这让我想到如果将变量命名为"self"这是否具有误导性?

读取方法集的规范并不提供任何证据(在我的解释中).

我似乎记得在某个地方发现它不是真正的自我指针,任何人都可以列出证据或提供任何推理方式,如果有任何问题/陷阱可能会因为将其视为"自我"而产生?

一个简单的例子:

type MyStruct struct {
    Name string
} 
Run Code Online (Sandbox Code Playgroud)

哪种方法更合适,或两者兼而有之?

func (m *MyStruct) MyMethod() error {
    // do something useful
}
Run Code Online (Sandbox Code Playgroud)

要么

func (self *MyStruct) MyMethod() error {
    // do something useful
}
Run Code Online (Sandbox Code Playgroud)

kos*_*tix 44

除了别人所说的话(尤其是PeterSOdskinner-in他对彼得的回答评论),注意几个重要的事情:

您可以像简单函数一样调用方法

在Go中,您可以调用任何方法函数而不是作为接收器上的方法,而是作为常规函数 - 只需通过使用其定义为类型的类型的名称来限定其名称并明确地将其传递给接收器参数(obtaininig)使用方法表达式调用方法中的简单函数.

展示:

package main

import "fmt"

type Foo int

func (f Foo) Bar() {
    fmt.Printf("My receiver is %v\n", f)
}

func main() {
    a := Foo(46)
    a.Bar()
    b := Foo(51)
    Foo.Bar(b)
}
Run Code Online (Sandbox Code Playgroud)

(游乐场链接.)

运行时,该程序打印:

My receiver is 46
My receiver is 51
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,self在这里失去了它的神圣含义,因为你刚刚调用了一种人工构造上下文的方法,它与被引用的"调用对象的方法将消息传递给该对象"概念无关.

回顾一下,在Go中,一个方法只是一个语义绑定到特定类型的函数,它接收一个额外的参数 - 它的接收器 - 无论它如何被调用.与许多其他主流语言相反,Go并没有将这一事实隐藏在地毯之下.

接收器在其类型定义的方法中不一定是可变的

正如我的例子所示,我已经Bar()在非指针接收器上定义了一个方法,如果你试图为接收器分配一个值,但是不会影响调用者,因为接收器 - 就像一切在Go中已经通过值传递(因此刚刚复制了整数).

为了能够在方法中改变接收者的值,你必须在适当类型的指针上定义它,就像

func (f *Foo) Bar() {
    // here you can mutate the value via *f, like
    *f = 73
}
Run Code Online (Sandbox Code Playgroud)

再次,您可以看到使用self含义"我","我的内部"在这里变得毫无意义:在我的示例中,该方法仅接收到它知道的类型的值.您可以看到这与许多OO语言形成对比,在这些语言中,对象是通常通过引用传递的黑盒子.在Go中,您可以在几乎任何东西上定义一个方法(包括net/http标准包使用的其他方法,顺便说一句),这会破坏"方法是用于对象"的概念.

不同的方法集可能适用于不同时间的相同值

在Go中,方法是围绕特定类型对功能进行分组的便捷方式,并且不同的方法集可能适用于程序流的不同点中的相同值.结合它们提供的接口和鸭子打字,这个概念真的很繁荣.我们的想法是,在Go中,有一种定义"支持"类型的习惯用法,这些类型对某些其他类型的值执行某些操作.

一个很好的例子是标准包sort:例如,它提供了IntSlice允许您对整数切片进行排序的类型 - 类型的值[]int.要做到这一点,你的片型转换为sort.IntSlice和你得到的结果值有一整套的同时,为您的排序方法切片你的价值的内部表示并没有改变 -因为sort.IntSlice定义为type IntSlice []int.在这种IntSlice类型的每种方法中,很难将其接收者值的含义与 - self简单地协调 - 因为该类型仅用于为另一种类型提供一组方法; 在哲学意义上,这种效用类型没有"自我"的概念;-)

结论

所以我要说的是,让事情变得简单,并且不要试图"重载"Go采用的明确而简单的方法,它没有明确说明它提供的语义.

再说一遍.在我学习Go时,我个人对Go的习语的看法是Go的最重要的属性是它的实用性(与理想主义等相反)所以如果你看到一些"感觉"不自然的概念,试着弄清楚为什么它是这样设计的,大多数通常你会发现为什么这个概念会"点击"你的大脑并变得自然.(我必须承认,为了解决Go中理解方法的这个特殊问题,一个良好的工作熟悉C会有很大的帮助.)

  • 但是python做同样的事情,对吧?foo.bar()只是Foo.bar(foo)的语法糖.不会使用它设置的标准(总是使用`self`)在这里有意义吗? (6认同)
  • 一个重要问题的一个很好的答案.这解决了我所拥有的"自我/"这个问题,同时提供了对语言机制的更深入理解.我希望在[Effective Go]上描述这些信息(https://golang.org/doc/effective_go.html#methods) (2认同)

Oli*_*man 15

我认为没有特别令人信服的理由来避免this/ self惯例.这里的其他帖子仅引用社区规范或描述与命名约定无关的方法调度方面.

这些代码审查指南拒绝thisself完全没有给出任何理由,除非您可以阅读隐含的声明中的某些内容,这些声明对方法的重视程度低于其他语言.

承诺thisself约定的一个优点是它有助于突出违反德米特定律的行为.代码如下:

func (w *ChunkWriter) Write(recId uint32, msg []byte) (recs uint64, err error) {
    recs = w.chunk.Records
    err = w.handle.Write(recId, msg)
    if err == nil {
        recs++
        w.chunk.Records = recs
    }
    return
}
Run Code Online (Sandbox Code Playgroud)

在它的表面上出现w不恰当地接触到其成员.实际上它正在访问其接收器的成员,这是完全正确的.

  • 好点。然而,Go 有私有/公共区别,但使用包而不是类作为边界 - 同一包中的任何函数/方法都可以访问“w.handle...”,这让我想知道如果 Demeter 法则应用于 Go 会如何反对这些但允许它在 ChunkWriter 方法中,因为它是“其接收者的成员”?如果 demeter "unit" == package,那么很容易“光学”应用 - 小写的 anyvar.anymember.Foo 仍然正确吗? (2认同)

det*_*000 14

转到维基建议不要使用类似的术语thisself和使用,而不是从类型名称派生缩写:https://github.com/golang/go/wiki/CodeReviewComments#receiver-names

  • 这些都是一些固执己见的人所为。每个人都有大脑。它们不一定是正确的。我一直用这个,我高兴极了。 (2认同)

RVJ*_*nan 12

我同意奥利弗·古德曼的回答,并钦佩他在面对如此巨大的困难时坚定信念的勇气。

基本上,反对者说使用“this”(我的偏好)作为接收器变量与其他语言的含义并不完全相同。因此,我们应该避免它以抢占潜在的陷阱并将混乱降到最低。

但是,当然,对于->and也可以这样说.,更不用说 Go 的许多其他类似 C 的方面(我们无法控制)。

但是,以 OP 暗示的方式使用“this”可以更容易地理解上下文。因为我们知道我们是用 Go 编程的,所以我们知道它与其他语言相比具有不同的含义。

使用“this”还意味着我们的类型(“类”)方法是“常规的”。如果预计会有一些奇怪的事情,那么您总是可以使用另一个接收器绰号来强调这一事实。

但是我们不要把婴儿和洗澡水一起倒掉!

从一开始,我就发现硬核 Gophers 往往非常僵硬和不屈不挠。但我必须承认,这在许多领域都很好地为语言服务。在这方面,我向他们致敬。

然而,这种不屈不挠的心态在其他领域也引发了问题,最明显的例子是 GOPATH 的疯狂。

听取专家意见不是不考虑问题的借口。即使是专家也有盲点和障碍,其大小往往与他们的专业成正比。

简短回答:使用最适合您的约定!

  • 答案和意见(在问题提出的一般背景下)! (3认同)
  • 这是一个答案还是您只是分享意见? (2认同)
  • 我同意,将接收器变量命名为“this”有助于传达函数的目的(纯粹作为实例方法),并且代码库的一致性将提高可读性和理解性。仅仅因为我们可以在不同的上下文中调用该函数,并不意味着我们不应该被允许在代码的上下文中表达该函数的有限意图。在Python中,我们通常在私有方法前加上下划线(即使它们仍然可以公开访问)来表达函数的意图。这有何不同? (2认同)