反射 reflection
- 通过反射可以动态的调用方法
- 反射可以通过
TypeOf
和ValueOf
函数从接口中获取目标对象信息 - 利用反射修改对象状态
- 反射会将匿名字段作为独立字段
通过反射获取对象的类型和值
示例如下:
package main
import (
"fmt"
"reflect"
)
type User struct {
Id int
Name string
Age int
}
func (u User) Hello(){
fmt.Println("Hello World")
}
//参数o : 可以接收任何类型
func Info(o interface{}){
//获取参数o的类型
t:= reflect.TypeOf(o)
//打印类型的名称
fmt.Printf("Type : %v\n", t.Name())
v := reflect.ValueOf(o)
fmt.Println("Fields:")
//遍历类型的字段
for i:=0;i<t.NumField();i++{
//根据索引取得字段
f := t.Field(i)
//取出字段对应的值
val := v.Field(i).Interface()
fmt.Printf("%5s: %v = %v\n",
f.Name, f.Type, val)
}
}
func main() {
u:= User{1,"Jack",22}
Info(u)
}
打印结果:
Type : User
Fields:
Id: int = 1
Name: string = Jack
Age: int = 22
获取方法信息
继续编写 Info 方法,片段如下:
...
//获取方法信息
for i := 0; i < t.NumMethod(); i++ {
m := t.Method(i)
fmt.Printf("%6s: %v\n", m.Name, m.Type)
}
打印结果:
Hello: func(main.User)
注意:
上述Info 方法接收的是值类型,是一个struct,如果是指针类型,就会报错,这里需要加入类型判断。
func main(){
u:= User{1,"Jack",22}
Info(&u)
}
报错如下:
panic: reflect: NumField of non-struct type
...
修改如下:
//参数o : 可以接收任何类型
func Info(o interface{}) {
//获取参数o的类型
t := reflect.TypeOf(o)
//判断参数o的类型,必须是struct
if k := t.Kind();k!= reflect.Struct{
fmt.Println("non-struct type")
return
}
...
反射结构中的嵌入字段
type Manager struct {
User //匿名字段
title string
}
func main() {
m := Manager{User: User{1, "Jack", 12}, title: "Student"}
t := reflect.TypeOf(m)
//取出匿名字段
fmt.Printf("%#v\n", t.Field(0))
//取到的是匿名字段User
fmt.Printf("%#v\n", t.FieldByIndex([]int{0}))
//取到的是匿名字段User的第一个属性Id
fmt.Printf("%#v\n", t.FieldByIndex([]int{0,0}))
//取到的是Manager的第二个属性title
fmt.Printf("%#v\n", t.FieldByIndex([]int{1}))
}
reflect.StructField{Name:"User", PkgPath:"", Type:(*reflect.rtype)(0x10b5c80), Tag:"", Offset:0x0, Index:[]int{0}, Anonymous:true}
reflect.StructField{Name:"User", PkgPath:"", Type:(*reflect.rtype)(0x10b5c80), Tag:"", Offset:0x0, Index:[]int{0}, Anonymous:true}
reflect.StructField{Name:"Id", PkgPath:"", Type:(*reflect.rtype)(0x10a29a0), Tag:"", Offset:0x0, Index:[]int{0}, Anonymous:false}
reflect.StructField{Name:"title", PkgPath:"main", Type:(*reflect.rtype)(0x10a30a0), Tag:"", Offset:0x20, Index:[]int{1}, Anonymous:false}
注意:返回的StructField的属性Anonymous ,其表示是否为匿名字段
通过反射修改基本类型的值
func main() {
x := 123
v := reflect.ValueOf(&x)
v.Elem().SetInt(456)
fmt.Println(x)
}
输出:
456
Elem返回v持有的接口保管的值的Value封装,或者v持有的指针指向的值的Value封装。如果v的Kind不是Interface或Ptr会panic;如果v持有的值为nil,会返回Value零值。
package main
import (
"fmt"
"reflect"
)
type User struct {
Id int
Name string
Age int
}
func main() {
u := User{1, "Jack", 22}
Set(&u)
fmt.Println(u)
}
func Set(o interface{}) {
v := reflect.ValueOf(o)
//类型必须是Ptr和可修改的
if v.Kind() != reflect.Ptr && !v.Elem().CanSet() {
fmt.Println("")
return
} else {
v = v.Elem()
}
if f:=v.FieldByName("Name");f.Kind()== reflect.String{
f.SetString("Bob")
}
}
输出:
{1 Bob 22}
上面的查询字段可以增加一个判断,判断字段是否有效,修改如下:
func Set(o interface{}) {
v := reflect.ValueOf(o)
//类型必须是Ptr和可修改的
if v.Kind() != reflect.Ptr && !v.Elem().CanSet() {
fmt.Println("")
return
} else {
v = v.Elem()
}
f:=v.FieldByName("Name1")
if !f.IsValid(){
fmt.Println("字段无效")
return
}
if f.Kind()== reflect.String{
f.SetString("Bob")
}
}
如何通过反射进行方法的调用
package main
import (
"fmt"
"reflect"
)
type User struct {
Id int
Name string
Age int
}
func (u User) Hello(name string){
fmt.Printf("Hello , %v . My Name is %v\n", name, u.Name)
}
func main() {
u:= User{1, "Jack",22}
v := reflect.ValueOf(u)
mv := v.MethodByName("Hello")
args := []reflect.Value{reflect.ValueOf("joe")}
mv.Call(args)
}