我在 Go 上看过相当多的博客和视频,据我记得,没有一个作者在编写方法时使用“self”或“this”作为接收器变量。然而,似乎有很多关于堆栈溢出的问题可以做到这一点,这让我思考这是否会误导将变量命名为“self”?
阅读方法集的规范并不能提供任何证据(在我的解释中)。
我似乎记得在某个地方发现它并不是真正的自我指针,任何人都可以列出证据或提供任何一种方式的推理,如果将其视为“自我”可能会出现任何问题/陷阱?
一个简单的例子:
type MyStruct struct {
Name string
}
哪种方法更合适,或两者兼而有之?
func (m *MyStruct) MyMethod() error {
// do something useful
}
要么
func (self *MyStruct) MyMethod() error {
// do something useful
}
除了其他人所说的(尤其是他PeterSO
对dskinner
彼得答案的评论),请注意几件重要的事情:
在 Go 中,您可以调用 任何 方法函数,而不是作为接收器上的方法,而是作为常规函数——这意味着通过使用它定义为方法的类型的名称来限定其名称并 显式 传递一个接收器参数 (使用方法表达式调用从方法中获取简单函数)。
展示:
package main
import "fmt"
type Foo int
func (f Foo) Bar() {
fmt.Printf("My receiver is %vn", f)
}
func main() {
a := Foo(46)
a.Bar()
b := Foo(51)
Foo.Bar(b)
}
(游乐场链接。)
运行时,该程序打印:
My receiver is 46
My receiver is 51
正如你所看到的,self
这里失去了它的神圣意义,因为你刚刚调用了一个 人为
地为其构造上下文的方法,这与被引用的“调用对象的方法是将消息传递给该对象”概念无关。
回顾一下,在 Go 中,方法只是一个在语义上绑定到特定类型的函数,它接收一个额外的参数——即接收者——不管它是如何调用的。与许多其他主流语言相反,Go 并没有隐藏这个事实。
如我的示例所示,我在Bar()
非指针接收器上定义了一个方法,如果您尝试为接收器分配一个值,该值将成功但不会影响调用方,因为接收Go
中的所有内容都是按值传递的(因此该整数刚刚被复制)。
为了能够在方法中改变接收者的值,你必须在一个适当类型的指针上定义它,比如
func (f *Foo) Bar() {
// here you can mutate the value via *f, like
*f = 73
}
同样,您可以看到使用self
“我”、“我的内部”的含义在这里变得没有意义:在我的示例中,该方法仅接收它知道的类型的值。您可以看到这与许多 OO
语言形成对比,在这些语言中,对象是通常通过引用传递的黑盒子。在 Go
中,您几乎可以在任何东西(包括标准包使用的其他方法)上定义一个方法,这会net/http
削弱“方法用于对象”的概念。
在 Go 中,方法是围绕特定类型对功能进行分组的便捷方式,不同的方法集可能适用于程序流不同点的相同值。结合他们提供的接口和鸭式打字,这个概念真的很繁荣。这个想法是,在 Go 中,有一个定义“支持”类型的习惯用法,这些类型对其他类型的值执行某些操作。
一个很好的例子是标准包sort
:例如,它提供了IntSlice
允许您对整数切片(type
的值)进行排序的类型[]int
。为此,您将切片类型转换为sort.IntSlice
,并且您获得的值具有一整套用于对切片进行排序的方法,而
您的值的内部表示没有改变 ——因为sort.IntSlice
定义为type IntSlice []int
.
在该IntSlice
类型的每个方法中,很难将其接收者值的含义与self
“暗示”相协调,因为该类型的存在只是为了为另一种类型提供一组方法;在哲学意义上,此类实用程序类型没有“自我”的概念;-)
所以我想说,让事情保持简单,不要试图用没有 明确 说明它提供的语义来“重载” Go 采用的清晰简单的方法。
再来一注。我对 Go 习语的个人看法是,Go 的最重要属性是它的实用性(与理想主义等相反),所以如果你看到一些“感觉”不自然的概念,试着弄清楚 为什么
它是这样设计的,而且大多数通常,您会发现为什么这个概念会在您的大脑中“点击”并变得自然。(我必须承认,要通过理解 Go
中的方法来解决这个特殊问题,对工作熟悉C
会有很大帮助。)