go 反射

Created

2024-10-28 13:25:08

Updated

2024-10-28 13:25:15

Tip

建议先看下接口章节

1 元数据

什么是元数据?

是描述数据的数据
比如一个结构体对象是个数据, 它是什么类型,这个类型就是用来描述这个它自己的一种数据
这个结构体对象的值,比如 Dog{Name:“tom”} tom 这种就是它的值, 它显然也是描述该结构体的一种数据
我们只要有了一个数据的类型和值, 那么我们就知道了该数据的全部信息

2 三定律

2.1 接口类型变量转换为反射对象

2.1.1 类型 reflect.Type

Tip
  • 是接口,用于获取元数据 : “类型”信息
  • reflect.TypeOf() 对象转换为Type接口的方法
Diagram
重要
func TypeOf(i any) Type {
    eface := *(*emptyInterface)(unsafe.Pointer(&i))
    return toType(eface.typ)
}
package test

import (
    "fmt"
    "reflect"
    "testing"
)
type Dog struct {
    Age  int
    name string
}

func (d Dog) shout() {
    println("wang ...")
}
func (d Dog) Show() {
    println("show ...")
}
func (d *Dog) SetName(name string) {
    d.name = name
}
func TestA(t *testing.T) {
    d := Dog{11, "tom"}
    typ := reflect.TypeOf(d)
    fmt.Println(typ.Name(), typ.Kind())
    if typ.Kind() == reflect.Struct {
        // 字段
        numField := typ.NumField()
        for i := 0; i < numField; i++ {
            // 可以获取 private 字段 信息
            fmt.Println(typ.Field(i).Name, "是否公开:", typ.Field(i).IsExported())
        }
        // 方法
        numMethod := typ.NumMethod()
        fmt.Println("方法个数:", numMethod) //1
        for i := 0; i < numMethod; i++ {
            // 只能获取public 方法 ,Show
            fmt.Println(typ.Method(i).Name)
        }
    }
}
func TestA(t *testing.T) {
    ptrD := &Dog{11, "tom"}
    ptrTyp := reflect.TypeOf(ptrD)
    // kind 是ptr指针
    fmt.Println(ptrTyp.Name(), ptrTyp.Kind())
    // 方法
    numMethod := ptrTyp.NumMethod()
    fmt.Println("方法个数:", numMethod) // 2
    for i := 0; i < numMethod; i++ {
        // SetName  Show (前面说过编译器会默认生成一个对应的 *Dog 为接受者的方法)
        fmt.Println(ptrTyp.Method(i).Name)
    }
    fmt.Println(ptrTyp.Elem().Kind()) // struct
}

2.1.2 值 reflect.Value

Tip
  • 是结构体,用于获取元数据: “值”信息
  • reflect.ValueOf() 对象转换为Value结构体的方法
Diagram
package test

import (
    "fmt"
    "reflect"
    "testing"
)
type Dog struct {
    Age  int
    name string
}

func (d Dog) shout() {
    println("wang ...")
}
func (d Dog) Show() {
    println("show ...")
}
func (d *Dog) SetName(name string) {
    d.name = name
}
func TestA(t *testing.T) {
    d := Dog{11, "tom"}
    val := reflect.ValueOf(d)
    typFromVal := val.Type()
    numField := val.NumField()
    for i := 0; i < numField; i++ {
        field := val.Field(i)
        typField := typFromVal.Field(i)
        if typField.IsExported() {
            // 私有的字段无法 获取它的值的
            // 值无法修改 ,因为 方法是值转递,无意义,指针的话就可以了
            fmt.Println(field.Interface(), field.CanSet())

        } else {
            fmt.Println(typField.Name)
        }
    }
}
func TestA(t *testing.T) {
    ptrD := &Dog{11, "tom"}
    ptrVal := reflect.ValueOf(ptrD)
    val = ptrVal.Elem()
    typFromVal = val.Type()
    numField = val.NumField()
    for i := 0; i < numField; i++ {
        field := val.Field(i)
        typField := typFromVal.Field(i)
        if typField.IsExported() {
            fmt.Println(field.Interface(), field.CanSet())
            field.SetInt(3)
            fmt.Println(ptrD.Age)
            // 或
            field.Set(reflect.ValueOf(5))
            fmt.Println(ptrD.Age)
        } else {
            fmt.Println(typField.Name)
        }
    }
    fmt.Println(ptrD.Age)
}

2.2 反射对象转换为接口类型变量

reflect.Interface()

转为接口类型的变量, 然后可通过类型断言等转为原来的类型

2.3 可变性

3 基础操作

type Dog struct {
    Age  int
    Name string
}
func (d Dog) SetName(name string) {
    d.Name = name
}
func TestReflect(t *testing.T) {
    d := &Dog{Age: 2, Name: "ff"}
    dT := reflect.TypeOf(d)
    for i := 0; i < dT.NumMethod(); i++ {
        // 打印方法
        fmt.Println(dT.Method(i).Name, "==", dT.Method(i).Type, "==", dT.Method(i).Type.Kind())
    }

    vv := reflect.ValueOf(d)
    args := []reflect.Value{reflect.ValueOf("tom")}
    // 调用方法
    vv.MethodByName(dT.Method(0).Name).Call(args)
    fmt.Println(d.Name)
}
Back to top