分享
  1. 首页
  2. 文章

golang反射

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

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

类型

  • 获取对象类型
    • 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

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

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏