go 结构体

Created

2024-10-28 13:26:12

Updated

2024-10-28 13:26:14

1 结构体和方法是如何联系的

结构体和方法是通过方法集(method set)联系起来的.方法集是一个特殊的函数指针,它指向结构体类型的方法. 当定义一个结构体类型时,Go语言会为这个类型生成一个方法集

反射来打印对象的方法
type Dog struct {
    Age  int
    Name string
}

func (d Dog) SetName(name string) {
    fmt.Println("input::", name)
    d.Name = name
}

func (d *Dog) GetName() string {
    fmt.Printf("方法内的地址: %p\n", d)
    return d.Name
}

func TestStruct(t *testing.T) {
    fmt.Println("Dog 类型的 方法")
    d := Dog{Age: 2}
    v := reflect.TypeOf(d)
    for i := 0; i < v.NumMethod(); i++ {
        // SetName == func(test.Dog, string) == func
        fmt.Println(v.Method(i).Name, "==", v.Method(i).Type, "==", v.Method(i).Type.Kind())
    }
    fmt.Println("*Dog 类型的方法")
    d2 := &Dog{}
    v2 := reflect.TypeOf(d2)
    for i := 0; i < v2.NumMethod(); i++ {
        // 注意有2个func, SetName(*Dog,string) 方法 自动生成了
        // 虽然我们只定义了 func SetName(Dog,string) 方法
        // GetName == func(*test.Dog) string == func
        // SetName == func(*test.Dog, string) == func
        fmt.Println(v2.Method(i).Name, "==", v2.Method(i).Type, "==", v2.Method(i).Type.Kind())
    }
}
重要结论

注意 编译器为 Dog 类型的 Shout方法 生成了 对应 *Dog类型 的Shout方法

命令查看实现的方法
go tool compile -S -N -l main.go
# 查看有哪些函数
go tool nm -type  main.o|grep ' T '

2 结构体方法的实质与参数

Tip

结构体调用方法实际上是函数的第一个参数是结构体

type Dog struct {
    Age  int
    Name string
}

func (d *Dog) GetName() string {
    // 与TestStruct2的 &d  地址一样
    fmt.Printf("方法内的地址: %p\n", d)
    return d.Name
}

func (d Dog) SetName(name string) {
    fmt.Println("input::", name)
    d.Name = name
}


// 前面通过反射我们看到实际上的方法是这样的
/*
func SetName(d Dog,name string) {
    d.Name = name
}

func GetName(d *Dog) string{
    return d.Name
}

结构体方法会自动生成一个结构体指针的方法
func SetName(d *Dog,name string) {
    d.Name = name
}
*/
func TestStruct2(t *testing.T) {
    d := Dog{Age: 3, Name: "hello"}
    fmt.Printf("外部的  地址:%p\n", &d)
    // 实际是将 &d 传递 给 GetName(&d)  ==> (*Dog).GetName(&d)
    println(d.GetName()) // hello
    // 等价于 执行   Dog.SetName(d, "abc")
    d.SetName("abc")     // SetName(d) , 由于go 是值传递的 ,所以没有改变d的Name属性
    println(d.Name)      // hello
}
func TestStruct2(t *testing.T) {
    f := &Dog{Age: 3, Name: "hello"}
    // 实际调用了 SetName(*f ,abc)
    // 按照前面说到的反射 应该有 SetName(*Dog,string) 方法
    // 但这里的调用还是用的  SetName(Dog,string) 方法!!!
    // 那么自动生成的 SetName(*Dog,string) 方法有什么用呢?
    f.SetName("abc")
    println(f.Name) // 还是 hello
}
Tip

编译器自动生成的 SetName(*Dog,string) 方法 用于接口,我们后续再说

Back to top