为什么可以将结构函数分配给其他结构的成员?

时间:2018-07-09 04:41:13

标签: go

我只是在弄乱,并写了下面的代码,

package main

import (
    "fmt"
)

type Person struct {
    name string
}

func (p Person) printName() {
    fmt.Println(p.name)
}

type Man struct {
    name string
    f func()
}

func main() {
    p := Person{name: "John"}
    m := Man{name: "Adam"}
    m.f = p.printName
    p.printName()
    m.f()
}

上面的代码导致以下输出。这也适用于所有程序包。

John
John

所以,这是我的问题。

  1. 为什么这样做?
  2. 结构方法需要相同类型的接收器。该函数仍如何访问Person结构的成员?
  3. 在上面的示例中执行m.f = p.printName时会发生什么?

3 个答案:

答案 0 :(得分:3)

这个问题主要涉及接收者,可能会扩展到嵌入。 来自relevant section of the spec

  

方法是具有接收器的函数。方法声明绑定了   标识符,方法名称,方法,并将该方法关联   带有接收者的基本类型。

     

MethodDecl =“ func”接收器MethodName签名[FunctionBody]。

     

Receiver = Parameters。

     

接收器是通过在   方法名称。该参数部分必须声明一个非变量   参数,接收者。其类型必须为T或* T   (可能使用括号),其中T是类型名称。表示的类型   用T表示的是接收方基本类型;它不能是指针或   接口类型,它必须与   方法。据说该方法绑定到基本类型和方法   名称仅在类型T或* T的选择器中可见。

     

方法中的非空白接收者标识符必须唯一   签名。如果没有在体内引用接收者的值   在方法中,其标识符可以在声明中省略。的   通常,这同样适用于函数和方法的参数。

     

对于基本类型,与其绑定的方法的非空白名称必须为   独特。如果基本类型是结构类型,则非空白方法和   字段名称必须是唯一的。

     

给出类型Point,声明

func (p *Point) Length() float64 {    
   return math.Sqrt(p.x * p.x + p.y *p.y) 
}

func (p *Point) Scale(factor float64) {   
   p.x *= factor  
   p.y *= factor
}
     

将接收器类型为* Point的方法Length和Scale绑定到   基本类型Point。

     

方法的类型是接收者为   第一个论点。

     

例如,方法Scale具有类型

     

func(p * Point,因子float64)

     

但是,以这种方式声明的函数不是方法。

Man有一个名为f的字段,该字段是不带任何参数且不返回任何内容的函数。 如上所见,golang内部对待

func (p Person) printName()

func printName(p Person)

,并且当它对Person结构起作用时,它可以被认为是没有参数的函数(并且这样做是因为p是Person而p.printName作用于p)。因此,可以将其分配给Man.f

因此,当您将Man结构上的f字段分配给一个函数时,该函数已经捕获了名称为“ John”的Person实例并从中读取名称,因此,您将获得打印第二个“ John”的效果。 Man.name字段从未在该字段上起作用。

我怀疑通过将Person结构嵌入到Man中可以实现正常行为。

here is a playground link to demonstrate that

答案 1 :(得分:2)

在Go中,方法基本上是带有receiver的函数。该接收器由编译器包装,除此之外,与普通函数没有什么不同。这意味着,无论您如何调用该方法,无论在何处,该方法总是可以获取它所绑定的接收者,将其分配给另一个变量或其他任何变量。

在您的代码中,f 不是类型Man的方法。它只是类型func()的字段。您可以将任何内容设置为与签名匹配的内容,并且该函数将对Man或其实例一无所知。这意味着m.f不了解m,也无法访问m.namem的任何其他字段。

还有一个注释,您可以调用以下方法:Person.PrintName(p),其中p的类型为Person

答案 2 :(得分:0)

Golang中的

函数也是一个值,它也可以作为参数传递或由其他函数分配,Golang中的方法也是一个函数,不同的是,它的接收者是它的值,因此在使用时将一个方法分配给一个具有相同签名的函数,它是指接收者,并以该方法中的代码执行目标函数