在Go里,receiver是关联在某个结构体上的,定义后,可以像方法一样调用它。例如,下面代码中的 func(d Dog)
package main
import "fmt"
type Dog struct {
}
func (d Dog) Say() {
fmt.Println("Woof!")
}
func main() {
d := &Dog{}
d.Say()
}
Go可以定义指针和非指针receiver,前者类似func (t *Type)
后者类似func (t Type)
,官网的这篇文章已经说明了两者具体的行为。
在C语言里,指针是要使用->操作符,而结构体方法的调用是使用.操作符,如下:
#include <stdio.h>
#include <stdlib.h>
struct Tree {
int a;
int b;
};
int main() {
struct Tree tree, *pointerToTree;
tree.a = 5;
tree.b = 7;
pointerToTree = malloc(sizeof(struct Tree));
pointerToTree->a = 5;
pointerToTree->b = 7;
printf("tree vals: %d %d\n", tree.a, tree.b);
printf("pointerToTree: %p %d %d\n", pointerToTree, pointerToTree->a, pointerToTree->b);
free(pointerToTree);
return 0;
}
Go类型系统有如下规定:
如果类型 x包含m方法,而且它的参数列表和m的参数列表一致,那么使用x.m()
调用该方法是有效的。如果x是可寻址的,且&x
的方法集合包含m,那么,x.m()
也可以调用,可以理解为是 (&x).m()
的简略形式。
Receiver 定义为指针或者非指针,会导致不同的行为。我们在做设计和考虑时,可以假设该 receiver 需要被当做参数来传递,可以按该假设将receiver定义为传值或传引用:
- receiver 需要被修改
- struct 比较大,深拷贝操作代价较大
- 一致性:如果struct 部分的方法有指针类型receiver,那么其余的也应该如此,这样struct的行为是比较明确的。
如果你的方法调用有以上三个特点,那么使用指针类型的receiver
package main
import "fmt"
type Mutatable struct {
a int
b int
}
func (m Mutatable) StayTheSame() {
m.a = 5
m.b = 7
}
func (m *Mutatable) Mutate() {
m.a = 5
m.b = 7
}
func main() {
m := &Mutatable{0, 0}
fmt.Println(m)
m.StayTheSame()
fmt.Println(m)
m.Mutate()
fmt.Println(m)
}
你会注意到,StayTheSame
定义了非指针的receiver,不会改变struct,和 Mutate
定义了指针类型,会改变struct。
提供Golang直播培训,3人开班,有需要请站短。