高效的 Go 编程 Effective Go - 方法
免费 - Sunrise - - 浏览量: 0 - 文章来源
指针 vs. 值
正如 ByteSize
那样,我们可以为任何已命名的类型(除了指针或接口)定义方法; 接收者可不必为结构体。
在之前讨论切片时,我们编写了一个 Append
函数。 我们也可将其定义为切片的方法。为此,我们首先要声明一个已命名的类型来绑定该方法, 然后使该方法的接收者成为该类型的值。
type ByteSlice []byte
func (slice ByteSlice) Append(data []byte) []byte {
// 主体与上面定义的Append函数完全相同。
}
我们仍然需要该方法返回更新后的切片。为了消除这种不便,我们可通过重新定义该方法, 将一个指向 ByteSlice
的指针作为该方法的接收者, 这样该方法就能重写调用者提供的切片了。
func (p *ByteSlice) Append(data []byte) {
slice := *p
// 主体同上,只是没有返回值
*p = slice
}
其实我们做得更好。若我们将函数修改为与标准 Write
类似的方法,就像这样,
func (p *ByteSlice) Write(data []byte) (n int, err error) {
slice := *p
// 同上。
*p = slice
return len(data), nil
}
那么类型 *ByteSlice
就满足了标准的 io.Writer
接口,这将非常实用。 例如,我们可以通过打印将内容写入。
var b ByteSlice
fmt.Fprintf(&b, "This hour has %d days\n", 7)
我们将 ByteSlice
的地址传入,因为只有 *ByteSlice
才满足 io.Writer
。以指针或值为接收者的区别在于:值方法可通过指针和值调用, 而指针方法只能通过指针来调用。
之所以会有这条规则是因为指针方法可以修改接收者;通过值调用它们会导致方法接收到该值的副本, 因此任何修改都将被丢弃,因此 Go 语言不允许这种错误。不过有个方便的例外:若该值是可寻址的, 那么该语言就会自动插入取址操作符来对付一般的通过值调用的指针方法。在我们的例子中,变量 b
是可寻址的,因此我们只需通过 b.Write
来调用它的 Write
方法,编译器会将它重写为 (&b).Write
。
顺便一提,在字节切片上使用 Write
的想法已被 bytes.Buffer
所实现。