golang 反射的使用

文章目录 (?) [+]

    package main
    
    import (
        "fmt"
        "go/ast"
        "reflect"
    )
    
    type Member struct {
        Id     int    `json:"id" orm:"member_id"`
        Name   string `json:"name"`
        status bool
    }
    
    func (m *Member) SetStatus(s bool) {
        m.status = s
    }
    
    func (m *Member) GetStatus() bool {
        return m.status
    }
    
    func (m Member) String() string {
        return fmt.Sprintf("id: %d, name: %s", m.Id, m.Name)
    }
    
    // 如果是值传递那么反射获取的对象将不能修改此值,否则 panic
    func Parse(v interface{}) {
        // 获取 v 的变量值,如果是地址传递获取的是指针,值传递则为变量值
        rValue := reflect.ValueOf(v)
        // 判断是否为一个指针,如果是指针就通过 Elem() 获取指针指向的变量值
        rValue = reflect.Indirect(rValue)
        // 获取 v 的变量类型
        rType := rValue.Type()
        // rType := reflect.TypeOf(v)
    
        switch rType.Kind() {
        case reflect.Struct:
            // 遍历结构体字段
            for i := 0; i < rValue.NumField(); i++ {
                field := rType.Field(i)
                // 忽略匿名字段和私有字段
                if !field.Anonymous && ast.IsExported(field.Name) {
                    // 获取结构体字段的 interface{} 变量
                    fmt.Println(rValue.Field(i).Interface())
                    // 对于值传递的结构体使用 Addr() 获取结构体字段 *interface{} 变量会报错
                    fmt.Println(reflect.TypeOf(rValue.Field(i).Addr().Interface()))
                    // 获取字段 tag
                    fmt.Println(rType.Field(i).Tag.Get("json"))
                }
            }
    
            // 根据字段名获取字段 interface{}
            fmt.Println(rValue.FieldByName("Name").Interface())
    
            // 获取非指针方法数量,案例中的 Member 的 String 方法
            fmt.Println(rValue.NumMethod())
            // 获取所有方法数量
            fmt.Println(rValue.Addr().NumMethod())
            // 遍历结构体方法
    
            rValue.Addr().MethodByName("SetStatus").Call([]reflect.Value{reflect.ValueOf(true)})
            fmt.Println(rValue.Addr().MethodByName("GetStatus").Call(nil))
            // 排序默认是按照函数名的排序(ASCII 码)
            fmt.Println(rValue.Addr().Method(2).Call(nil))
            fmt.Println(rValue.Method(0).Call(nil))
    
            // 创建一个新反射对象
            fmt.Println(reflect.New(rType).Elem().FieldByName("Id"))
        case reflect.Func:
            num := 2
            argv := make([]reflect.Value, rType.NumIn())
            for i := range argv {
                if rType.In(i).Kind() == reflect.Int {
                    argv[i] = reflect.ValueOf(num)
                }
            }
            result := rValue.Call(argv)
            if len(result) == 1 {
                fmt.Println(result[0].Int())
            }
        case reflect.Int:
            // 修改值
            rValue.SetInt(127)
            // 从反射对象获取 interface{} 变量
            rIntf := rValue.Interface()
            // 根据 interface{} 进行类型断言
            if num, ok := rIntf.(*int); ok {
                fmt.Println(num)
            }
        case reflect.Slice:
            // 切片类型
            fmt.Println(rType.Kind())
            // 切片内元素类型
            // Elem() 作用是返回接口 v 包含的值或 v 指向的指针
            fmt.Println(rType.Elem().Kind())
        }
    }
    
    func Add(a, b int) int {
        return a + b
    }
    
    func main() {
        m := &Member{
            Id:   23,
            Name: "jack",
        }
        Parse(m)
        fmt.Println(m.GetStatus())
    
        // 函数调用
        Parse(Add)
    
        num := 6
        // 如果为值传递修改时会 panic
        Parse(&num)
        fmt.Println(num)
    
        var ms []Member
        Parse(&ms)
    }
    /*
    23
    *int
    id
    jack
    *string
    name
    jack
    1
    3
    [<bool Value>]
    [id: 23, name: jack]
    [id: 23, name: jack]
    0
    true
    4
    127
    slice
    struct
    */


    本文标题:golang 反射的使用
    本文链接:https://www.lanseyujie.com/post/golang-reflection.html
    版权声明:本文使用「署名-非商业性使用-相同方式共享」创作共享协议,转载或使用请遵守署名协议。
    点赞 0 分享 0