猿问

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

我只是在阅读“有效执行”并在“指针与值”部分中接近结尾处说:

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

为了测试它,我这样写:

package main


import (

  "fmt"

  "reflect"

)


type age int


func (a age) String() string {

  return fmt.Sprintf("%d yeasr(s) old", int(a))

}


func (a *age) Set(newAge int) {

  if newAge >= 0 {

    *a = age(newAge)

  }

}


func main() {

  var vAge age = 5

  pAge := new(age)


  fmt.Printf("TypeOf =>\n\tvAge: %v\n\tpAge: %v\n", reflect.TypeOf(vAge),

    reflect.TypeOf(pAge))


  fmt.Printf("vAge.String(): %v\n", vAge.String())

  fmt.Printf("vAge.Set(10)\n")

  vAge.Set(10)

  fmt.Printf("vAge.String(): %v\n", vAge.String())


  fmt.Printf("pAge.String(): %v\n", pAge.String())

  fmt.Printf("pAge.Set(10)\n")

  pAge.Set(10)

  fmt.Printf("pAge.String(): %v\n", pAge.String())

}

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


大话西游666
浏览 227回答 2
2回答

白衣染霜花

这是有效的,因为它vAge是可寻址的。请参阅语言规范下的“通话”中的最后一段:如果x的方法集包含m并且参数列表可以分配给m的参数列表,则方法调用xm()是有效的。如果x是可寻址的并且&x的方法集包含m,则xm()是(&x).m()的简写。

扬帆大鱼

vAge不被视为仅是“值变量”,因为它是内存中存储type值的已知位置age。纵观vAge只能作为它的价值,vAge.Set(10)是不是因为对自己的表现有效的,但由于vAge是可寻址的,该规范宣称它的好来治疗表达简写为“获得vAge的地址,并调用设置该”编译-time时,我们将能够验证这Set是为age或方法设置的一部分*age。您基本上允许编译器在确定必要和可能的情况下对原始表达式进行文本扩展。同时,编译器将允许您调用,age(23).String()但不允许age(23).Set(10)。在这种情况下,我们正在使用type的不可寻址值age。由于说的无效&age(23),所以说的无效(&age(23)).Set(10); 编译器不会进行该扩展。查看Effective Go示例,您并没有直接调用b.Write()我们知道b的完整类型的范围。相反,您是制作的临时副本,b并尝试将其作为type的值传递出去interface io.Writer()。问题在于,实现Printf不会对传入的对象一无所知,只是它承诺会知道如何接收Write(),因此它不知道在调用函数之前将abyteSlice转换为a即可*ByteSlice。是否寻址的决定b必须在编译时进行,PrintF其前提是其第一个参数知道如何在Write()不被引用的情况下进行接收。您可能会认为,如果系统知道如何获取age指针并将其转换为age值,那么它应该可以执行相反的操作;但是,实际上并没有意义。在Effective Go示例中,如果要传递b而不是&b,则将修改一个在PrintF返回后将不再存在的切片,这几乎没有用。在age上面的示例中,采用值23并用value覆盖它实际上没有任何意义10。在第一种情况下,让编译器停止并询问程序员在移交时真正意味着什么是有意义的b。在后一种情况下,对于编译器而言,拒绝修改常量值当然是有意义的。此外,我不认为系统正在动态地将age的方法设置扩展为*age;。我的疯狂猜测是,为每种基本类型的方法静态地赋予了指针类型一个方法,该方法只是取消引用指针并调用该基本方法。自动执行此操作很安全,因为按值接收方法中的任何内容都无法更改指针。在另一个方向上,通过包装被要求修改数据的方法来扩展一组要求修改数据的方法并不总是很有意义,因为它们所修改的数据此后不久就会消失。在某些情况下,这样做确实是有意义的,但是这需要程序员明确地决定,并且编译器停止并要求这样做是有意义的。tl; dr我认为,Effective Go中的段落可能需要重新措辞(尽管我可能太long了以至于无法胜任),但这是正确的。类型的指针*X实际上可以访问的所有X方法,但“ X”不能访问*X的。因此,当确定一个对象是否可以满足给定的接口时,*X被允许满足X可以实现的任何接口,但是反之则不成立。此外,即使X已知作用域中的类型变量在编译时是可寻址的,所以编译器可以将其转换为*X-它会出于接口实现的目的而拒绝这样做,因为这样做可能没有意义。 。
随时随地看视频慕课网APP

相关分类

Go
我要回答