golang反射

darcyaf · · 622 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

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

类型

  • 获取对象类型
    • 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!"))
}

性能


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:darcyaf

查看原文:golang反射

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

622 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传