为什么在使用指针接收器的方法时,对象状态在映射内部调用时不会改变?

huangapple go评论117阅读模式
英文:

Why does object state not change when calling methods with a pointer receiver from within a map?

问题

考虑以下代码:

package main

import (
	"fmt"
)

type Person struct {
	Age int
	M   map[string]func()
}

func New() Person {
	p := Person{1, make(map[string]func())}
	p.M["1"] = p.Add
	return p
}

func (p *Person) Add() {
	fmt.Println("在 Add 中修改前的年龄:", p.Age)
	p.Age += 1 // 这里年龄应该变为 3
	fmt.Println("在 Add 中修改后的年龄:", p.Age)
}

func (p *Person) Run() {
	fmt.Println("在 Run 中修改前的年龄:", p.Age)
	p.Age += 1 // 这里年龄应该是 2
	p.M["1"]()
	fmt.Println("在 Run 中修改后的年龄:", p.Age)
}

func main() {
	p := New()
	fmt.Println("调用 Run 前的年龄:", p.Age)
	p.Run()
	fmt.Println("调用 Run 后的年龄:", p.Age)
}

这段代码的输出结果是:

调用 Run 前的年龄: 1
在 Run 中修改前的年龄: 1
在 Add 中修改前的年龄: 1
在 Add 中修改后的年龄: 2
在 Run 中修改后的年龄: 2
调用 Run 后的年龄: 2

在这里,M 是结构体的一个映射成员,它存储了相同结构体的方法,并与字符串关联。

main 函数中,我们实例化了一个非指针的结构体值。

然后我们在该结构体上调用了一个名为 Run 的方法,该方法被定义为具有指针接收器。这应该与该方法共享指向结构体的指针,从而允许该方法修改原始结构体。

Run 方法内部,我们通过映射调用了存储在映射中的 Add 函数。但是这并没有修改结构体,实际上它似乎是在新创建时的对象状态上操作的。

Run 结束时,Add 中的任何更改都会丢失,尽管 Add 也是使用指针接收器声明的。

我怀疑这是因为在调用 p.M["1"] = p.Add 时,原始对象被某种方式复制到了映射中?

为什么使用指针接收器调用方法不会修改对象呢?

Playground 链接:https://play.golang.org/p/CRer_rT8_sj

英文:

Consider the following code:

package main

import (
	"fmt"
)

type Person struct {
	Age int
	M   map[string]func()
}

func New() Person {
	p := Person{1, make(map[string]func())}
	p.M["1"] = p.Add
	return p
}

func (p *Person) Add() {
	fmt.Println("Age before mutating in Add", p.Age)
	p.Age += 1 // here age should become 3
	fmt.Println("Age after mutating in Add", p.Age)
}

func (p *Person) Run() {
	fmt.Println("Age before mutating in Run", p.Age)
	p.Age += 1 // here age should be 2
	p.M["1"]()
	fmt.Println("Age after mutating in Run", p.Age)
}

func main() {
	p := New()
	fmt.Println("age before", p.Age)
	p.Run()
	fmt.Println("age after", p.Age)
}

This produces output

age before 1
Age before mutating in Run 1
Age before mutating in Add 1
Age after mutating in Add 2
Age after mutating in Run 2
age after 2

Here M is a map member of a struct, it stores methods of the same struct against strings.

In main we instantiate a non pointer struct value.

Then we call a method Run on that struct which is defined to have a pointer receiver. This should share a pointer to the struct with the method, which should allow the method to mutate the original struct.

Within Run we call the function Add stored within the map, via the map. But this does not mutate the struct, in fact it seems to operate on the state of the object as it was when newly created.

Any changes within Add are lost by the time Run ends, although Add is also declared with a pointer receiver.

I suspect this is because the original object was copied into the map somehow when calling p.M["1"] = p.Add?

Why does calling the method with pointer receiver not mutate the object?

Playground link https://play.golang.org/p/CRer_rT8_sj

答案1

得分: 3

New 返回的是一个 Person 而不是 *Person

当你写下

p := New()

它首先通过 New 构造一个名为 pnewPerson,并将其 Add 方法添加到 pnew.M 中。然后它返回 pnew

然后将这个 pnew 赋值给 p,它是一个 不同的 变量。所以 p.M 现在包含了 pnewAdd 方法。当你执行 p.M["1"]() 时,在 RunpnewAge 会增加(这与 p 的年龄无关)。

英文:

New returns a Person and not a *Person.

When you write

p := New()

it starts with New constructing a Person pnew and adding its Add method to pnew.M. Then it returns pnew.

Then this pnew is assigned to p which is a different variable. So p.M contains now pnew's Add method. When you execute p.M["1"]() in Run pnew's Age is incremented (which is unrelated to p's age.

huangapple
  • 本文由 发表于 2021年11月18日 19:51:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/70019353.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定