模块 5 · 第 2 课(共 5)课程中的22/50~14 min
模块课程(2/5)
方法:值与指针接收器
Go 中的 方法 是一个带有 接收器 的函数:一个特殊的
在 func 和方法名称之间声明的参数,该参数绑定
方法到类型。没有类:可以定义方法
当前包中声明的任何命名类型。
句法
Go
type Person struct {
Name string
}
func (p Person) Greet() string {
return "ciao " + p.Name
}
p := Person{Name: "Ada"}
fmt.Println(p.Greet()) // "ciao Ada"接收方 p Person 使 Greet 成为 Person 的方法。里面的
方法中,p 是一个与其他变量一样的变量。
值接收器与指针接收器
Go
// VALUE receiver: operates on a COPY of the value
func (p Person) NameUpper() string {
return strings.ToUpper(p.Name)
}
// POINTER receiver: operates on the original value, can mutate it
func (p *Person) Rename(n string) {
p.Name = n
}使用指针接收器,您可以修改接收器和更改 在方法外部可见。对于价值接收者,你不能——你会 只修改本地副本。
Go
p := Person{Name: "Ada"}
p.Rename("Grace")
fmt.Println(p.Name) // "Grace" — change is visible何时使用指针接收器
实用规则(按优先顺序排列):
- 必须修改接收者→指针接收者。强制的。
- 结构体很大(里面有几十个字段、数组)→指针 接收器以避免昂贵的副本。
- 一致性:如果类型上至少有一个方法具有指针接收器,请使用 也是其他人的指针接收器。一个非常重要的会议 接口(模块 6)。4.小型、不可变的“值”类型(time.Time、复数、小型 structs) → 值接收器,更具可读性。
非结构类型的方法
您可以在包中声明的任何 NAMED 类型上定义方法:
Go
type Celsius float64
func (c Celsius) ToFahrenheit() float64 {
return float64(c)*9/5 + 32
}
t := Celsius(20)
fmt.Println(t.ToFahrenheit()) // 68您不能在其他包的类型上定义它们(例如 int、string):您必须
首先创建一个本地命名类型。
尝试一下
锻炼#go.m5.l2.e1
尝试:0加载中...
将 Greet() 字符串方法(值接收器)添加到返回“ciao <Name>”的 Person。
正在加载编辑器...
显示提示
接收者位于方法名称之前的括号中。
3 次尝试后可用的解决方案
锻炼#go.m5.l2.e2
尝试:0加载中...
添加带有更改 p.Name 的 POINTER 接收器的 Rename(n string) 方法。
正在加载编辑器...
显示提示
指针接收者:`(p *Person)`。
3 次尝试后可用的解决方案
Quiz#go.m5.l2.e3
准备好什么时候应该使用指针接收器 (p *T)?
Go
func (t ?T) M() { ... }回顾
- 方法=带有接收器的函数:
func (r T) Name(...) ... { ... }。 - 值接收者:对副本进行操作;指针接收器:对原件进行操作。
- Go 在调用站点自动执行
T↔*T转换。 - 指针接收器,当:突变+大结构+与该类型的其他方法一致时。
- 切勿在同一类型上混合使用值接收器和指针接收器。
- 方法适用于当前包中的命名类型(包括非结构体)。