跳转到主要内容
模块 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

何时使用指针接收器

实用规则(按优先顺序排列):

  1. 必须修改接收者→指针接收者。强制的。
  2. 结构体很大(里面有几十个字段、数组)→指针 接收器以避免昂贵的副本。
  3. 一致性:如果类型上至少有一个方法具有指针接收器,请使用 也是其他人的指针接收器。一个非常重要的会议 接口(模块 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 转换。
  • 指针接收器,当:突变+大结构+与该类型的其他方法一致时。
  • 切勿在同一类型上混合使用值接收器和指针接收器。
  • 方法适用于当前包中的命名类型(包括非结构体)。