美文网首页
golang反射

golang反射

作者: darcyaf | 来源:发表于2020-03-09 14:05 被阅读0次

    反射让我们能在运行期间弹指对象的类型信息和内存结构,甚至还能访问隐藏属性。

    类型

    • 获取对象类型
      • t.Name是真实类型
      • t.Kind是基础结构类型
    package main
    
    import (
        "fmt"
        "reflect"
    )
    
    type B func(int) int
    
    func main() {
        var b B
        t := reflect.TypeOf(b)
        fmt.Println(t.Name(), t.Kind()) //  B func
    }
    
    • 构建对象
    package main
    
    import (
        "fmt"
        "reflect"
    )
    
    func main() {
        a := reflect.ArrayOf(1, reflect.TypeOf(byte(0)))
        m := reflect.MapOf(reflect.TypeOf(""), reflect.TypeOf(0))
        fmt.Println(a, m) //[1]uint8 map[string]int
    }
    
    • 指针类型和t.Elem()
    • 指针类型类似于*int
    • t.Elem() 返回指针对应的数据类型
    package main
    
    import (
        "fmt"
        "reflect"
    )
    
    func main() {
        x := 100
        tx, tp := reflect.TypeOf(x), reflect.TypeOf(&x)
        fmt.Println(x, tp, tx == tp)
        fmt.Println(tx.Kind(), tp.Kind())
        fmt.Println(tx == tp.Elem())
    }
    // 100 *int false
    // int ptr
    // true
    
    • 遍历结构体 f := t.Field(i)来获取字段,也可通过t.FieldByIndex([]int{0,1})根据索引查找,也可通过t.FieldByName("name")方式查找,不过fieldByName有同名遮盖,不支持多级名称
    package main
    
    import (
        "fmt"
        "reflect"
    )
    
    type user struct {
        name string
        age  int
    }
    
    type manager struct {
        *user
        title string
    }
    
    func main() {
        var m manager
        t := reflect.TypeOf(&m)
        if t.Kind() == reflect.Ptr { //获取指针的基类型
            t = t.Elem()
        }
        for i := 0; i < t.NumField(); i++ {
            f := t.Field(i)
            fmt.Println(f.Name, f.Type, f.Offset)
    
            if f.Anonymous { //匿名字段结构
                ft := f.Type
                if f.Type.Kind() == reflect.Ptr {
                    ft = ft.Elem()
                }
                for x := 0; x < ft.NumField(); x++ {
                    af := ft.Field(x)
                    fmt.Println(" ", af.Name, af.Type)
                }
            }
        }
    }
    
    • 获取tag
      f.Tag.Get("field")
    • implement关系
    import (
        "fmt"
        "reflect"
    )
    
    type X int
    
    func (X) String() string {
        return ""
    }
    
    func main() {
        var a X
        t := reflect.TypeOf(a)
        st := reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
        fmt.Println(t.Implements(st))
        it := reflect.TypeOf(0)
        fmt.Println(t.ConvertibleTo(it))
        fmt.Println(t.AssignableTo(st), t.AssignableTo(it))
    }
    // true
    // true
    // true false
    

    跟Type 获取类型信息不同,Value专注于对象实例数据读写

    • CanAddr,CanSet
    func main() {
        a := 100
        va, vp := reflect.ValueOf(a), reflect.ValueOf(&a).Elem()
        fmt.Println(va.CanAddr(), va.CanSet())
        fmt.Println(vp.CanAddr(), vp.CanSet())
    }
    // false false
    // true true
    
    
    • 不能对非导出字段直接进行设置操作
    type User struct {
        Name string
        code int
    }
    
    func main() {
        p := new(User)
        v := reflect.ValueOf(p).Elem()
    
        name := v.FieldByName("Name")
        code := v.FieldByName("code")
    
        fmt.Printf("name: canaddr=%v, canset=%v\n", name.CanAddr(), name.CanSet())
        fmt.Printf("code: canaddr=%v, canset=%v\n", code.CanAddr(), code.CanSet())
    }
    //name: canaddr=true, canset=true
    //code: canaddr=true, canset=false
    
    • nil
    1. 对于有类型的nil, 可通过 reflect.Value(b).isNil来判断
    type User struct {
        Name string
        code int
    }
    
    func main() {
        var a interface{} = nil
        var b interface{} = (*int)(nil)
        fmt.Println(a == nil)
        fmt.Println(b == nil, reflect.ValueOf(b).IsNil())
    }
    // true
    // false true
    
    
    1. 也可以用unsafe转换后直接判断iface.data是否为零值
    func main() {
        var b interface{} = (*int)(nil)
        iface := (*[2]uintptr)(unsafe.Pointer(&b))
        fmt.Println(iface, iface[1] == 0)
    }
    //&[4822752 0] true
    

    方法

    通过MethodByName获取,通过Call()调用, 对于变参可以用CallSlice,无法调用非导出方法

    type X struct {
    }
    
    func (X) Test(x, y int) (int, error) {
        return x + y, fmt.Errorf("err: %d", x+y)
    }
    
    func main() {
        var a X
        v := reflect.ValueOf(&a)
        m := v.MethodByName("Test")
        in := []reflect.Value{
            reflect.ValueOf(1), reflect.ValueOf(2),
        }
        out := m.Call(in)
        for _, v := range out {
            fmt.Println(v)
        }
    }
    

    构建

    func add(args []reflect.Value) (results []reflect.Value) {
        if len(args) == 0 {
            return nil
        }
        var ret reflect.Value
        switch args[0].Kind() {
        case reflect.Int:
            n := 0
            for _, a := range args {
                n += int(a.Int())
            }
            ret = reflect.ValueOf(n)
        case reflect.String:
            ss := make([]string, 0, len(args))
            for _, s := range args {
                ss = append(ss, s.String())
            }
            ret = reflect.ValueOf(strings.Join(ss, ""))
        }
        results = append(results, ret)
        return
    }
    func makeAdd(fptr interface{}) {
        fn := reflect.ValueOf(fptr).Elem()
        v := reflect.MakeFunc(fn.Type(), add)
        fn.Set(v)
    }
    func main() {
        var intAdd func(x, y int) int
        var strAdd func(a, b string) string
        makeAdd(&intAdd)
        makeAdd(&strAdd)
    
        println(intAdd(100, 200))
        println(strAdd("hello", " world!"))
    }
    

    性能

    相关文章

      网友评论

          本文标题:golang反射

          本文链接:https://www.haomeiwen.com/subject/vdgjdhtx.html