Go接收器方法调用语法混乱

Go接收器方法调用语法混乱,go,Go,我刚刚通读了一遍,在结尾附近的部分中,它说: 关于指针与接收器值的规则是,可以对指针和值调用值方法,但只能对指针调用指针方法。这是因为指针方法可以修改接收器;在值的副本上调用它们将导致放弃这些修改 为了测试它,我写了以下内容: 主程序包 进口( “fmt” “反映” ) 类型年龄整型 func(a age)String()字符串{ 返回fmt.Sprintf(“%d年旧”,int(a)) } func(a*age)集(newAge int){ 如果newAge>=0{ *a=年龄(新年龄) }

我刚刚通读了一遍,在结尾附近的部分中,它说:

关于指针与接收器值的规则是,可以对指针和值调用值方法,但只能对指针调用指针方法。这是因为指针方法可以修改接收器;在值的副本上调用它们将导致放弃这些修改

为了测试它,我写了以下内容:

主程序包
进口(
“fmt”
“反映”
)
类型年龄整型
func(a age)String()字符串{
返回fmt.Sprintf(“%d年旧”,int(a))
}
func(a*age)集(newAge int){
如果newAge>=0{
*a=年龄(新年龄)
}
}
func main(){
var迷走神经年龄=5
页码:=新(年龄)
fmt.Printf(“TypeOf=>\n\t页面:%v\n\t页面:%v\n”,reflect.TypeOf(vAge),
反映。类型(第页)
fmt.Printf(“vAge.String():%v\n”,vAge.String())
fmt.Printf(“变量集(10)\n”)
迷走神经套(10)
fmt.Printf(“vAge.String():%v\n”,vAge.String())
fmt.Printf(“pAge.String():%v\n”,pAge.String())
fmt.Printf(“页面集(10)\n”)
第页第10页
fmt.Printf(“pAge.String():%v\n”,pAge.String())
}

它可以编译,即使文档说它不应该编译,因为指针方法
Set()
不应该通过值var
vAge
调用。我在这里做错了什么吗?

这是正确的,因为
vAge
是可寻址的。请参见语言规范中的最后一段:

如果方法集(类型)为x,则方法调用x.m()有效 包含m,参数列表可以指定给参数列表 m。如果x是可寻址的,并且&x的方法集包含m,则x.m()是可寻址的 (&x).m()的简写


vAge
不被认为只是一个“值变量”,因为它是内存中存储
age
类型值的已知位置。仅将
vAge
视为其值,
vAge.Set(10)
本身作为表达式是无效的,但由于
vAge
是可寻址的,规范声明可以在编译时将表达式视为“获取vAge的地址,并在该地址上调用Set”的缩写,当我们能够验证
Set
age
*age
方法集的一部分时。您基本上允许编译器在原始表达式上进行文本扩展,如果它确定这是必要的和可能的

同时,编译器将允许您调用
age(23).String()
,但不允许调用
age(23).Set(10)
。在本例中,我们使用的是类型为
age
的不可寻址值。因为说
&age(23)
无效,所以说
(&age(23))无效。Set(10)
;编译器不会执行该扩展

看看有效的Go示例,您没有在我们知道
b
的完整类型的范围内直接调用
b.Write()
。而是创建
b
的临时副本,并尝试将其作为
interface io.Writer()类型的值进行传递。问题是,
Printf
的实现除了承诺它知道如何接收
Write()。是否寻址
b
的决定必须在编译时进行,编译
PrintF
的前提是它的第一个参数将知道如何在不被引用的情况下接收
Write()

您可能会认为,如果系统知道如何获取
age
指针并将其转换为
age
值,那么它应该能够执行相反的操作;但是,能够做到这一点并没有什么意义。在有效的Go示例中,如果要通过
b
而不是
&b
,则需要修改PrintF返回后不再存在的切片,这几乎没有什么用处。在我上面的
age
示例中,取
23
值并用
10
值覆盖它实际上毫无意义。在第一种情况下,编译器停止并询问程序员在关闭
b
时真正想做什么是有意义的。在后一种情况下,编译器拒绝修改常量值当然是有意义的

此外,我不认为系统正在动态扩展
age
的方法设置为
*age
;我的猜测是,指针类型静态地为每个基类型的方法提供了一个方法,它只是取消引用指针并调用基的方法。自动执行此操作是安全的,因为按值接收方法中的任何内容都不能更改指针。在另一个方向上,扩展一组请求修改数据的方法并不总是有意义的,方法是将它们包装成它们修改的数据很快就会消失。当然,在某些情况下这样做是有意义的,但这需要由程序员明确决定,编译器停止并要求这样做也是有意义的

tl;博士,我认为《有效围棋》中的这一段可能需要一些改写(尽管我可能太啰嗦了,无法胜任这项工作),但它是正确的。类型为
*X
的指针可以有效地访问
X
的所有方法,但“X”不能访问
*X
。因此,当确定一个对象是否可以实现给定的接口时,
*X
允许实现任何接口
X
可以实现的接口,但反之不成立。此外,即使已知作用域中的
X
类型的变量在编译时是可寻址的(因此编译器可以将其转换为
*X
),出于接口实现的目的,它也会拒绝这样做,因为这样做可能没有意义。

我不会这么说