反射让我们能在运行期间弹指对象的类型信息和内存结构,甚至还能访问隐藏属性。
类型
- 获取对象类型
- 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
- 对于有类型的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
- 也可以用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!"))
}
性能
有疑问加站长微信联系(非本文作者)