Go 语言学习之 reflect
概念:
在Go 语言中,reflect 可以用来在运行期间获取对象的类型信息和内存架构,reflect 操作所需的全部信息都源自接口变量,接口变量除了存储自身类型外,还会保存实际对象的类型数据,reflect通过TypeOf 和ValueOf 函数,将任何传入的对象转换位接口类型。
使用反射获取对象的类型:
func reflect.TypeOf(i interface{}) reflect.Type
获取对象的类型信息
type A intfunc main() {var a A = 1t := reflect.TypeOf(a)fmt.Println(t.Name(), t.Kind())
}
输出的结果:
需要注意的是,Name是真实类型,Kind 是真实类型的基础结构类别,在类型判断时要注意区分。
传入的参数对象区分基本类型和指针类型
func main() {b := 2t1, t2 := reflect.TypeOf(b), reflect.TypeOf(&b)fmt.Println(t1, t2, t1 == t2, t2.Elem())fmt.Println(t1.Kind(), t2.Kind())fmt.Println(t1 == t2.Elem())
}
输出的结果:
上述的代码中,我们使用看Elem方法返回指针的基类型,此外,Elem 方法还可以返回数据,slice 、map 、chan 的基类型。
使用反射操作struct
遍历结构体:
遍历结构体的字段,需要先获取结构体指针的基类型
type student struct {name stringscore int
}func main() {var s studentt := reflect.TypeOf(&s)if t.Kind() == reflect.Ptr {t = t.Elem()}fmt.Println(t.NumField())for i := 0; i < t.NumField(); i++ {structField := t.Field(i)fmt.Println(structField.Name, structField.Type)}
}
输入结果:
提取struct tag
一般用于ORM 映射,数据格式验证。
type user struct {name string `form:"user_name"`age int `form:"user_age"`
}func main() {var user1 usert := reflect.TypeOf(user1)for i := 0; i < t.NumField(); i++ {structField := t.Field(i)fmt.Println(structField.Name, structField.Tag.Get("form"))}
}
输出结果:
使用反射获取和修改对象的值:
func reflect.ValueOf(i interface{}) reflect.Value
type animal struct {Name stringage int
}func main() {var cat animalv := reflect.ValueOf(&cat).Elem()fmt.Printf("v:%v type:%v kind:%v\n", v, v.Type(), v.Kind())v.FieldByName("Name").SetString("kity")fmt.Printf("v:%v type:%v kind:%v\n", v, v.Type(), v.Kind())
}
输出结果:
通过代码演示,发现ValueOf 可以获取对象的值
我们知道修改对象的变量,需要传入指针,但是因为接口变量存储的指针的不可以寻址和设置变量的,所以还需要通过Elem 获取目标对象,
非导出字段的值不能修改
使用反射动态调用方法:
固定参数,使用call ,只需按照参数雷彪传参即可
演示代码:
type member struct {
}func (m member) MemberInfo(name string, score int) string {mInfo := fmt.Sprintf("姓名:%s 积分:%d\n", name, score)return mInfo
}func main() {var m1 memberv := reflect.ValueOf(&m1)m := v.MethodByName("MemberInfo")param := []reflect.Value{reflect.ValueOf("frank"),reflect.ValueOf(88),}result := m.Call(param)for _, v := range result {fmt.Println(v)}
}
输出结果:
可变参数,使用CallSlice,仅需一个 []interface{} 即可。
演示代码:
type person struct {
}func (p person) Hobby(name string, hobby ...interface{}) string {myHooby := fmt.Sprintf("I am %v, My hooby is %v", name, hobby)return myHooby
}func main() {var p1 personv := reflect.ValueOf(&p1)m := v.MethodByName("Hobby")param := []reflect.Value{reflect.ValueOf("lucy"),reflect.ValueOf("singing"),reflect.ValueOf("dancing0"),}result := m.Call(param)fmt.Println(result)param = []reflect.Value{reflect.ValueOf("lucy"),reflect.ValueOf([]interface{}{"singing", "dancing"}),}result = m.CallSlice(param)fmt.Println(result)
}
输出结果: