上一章:Chapter016 goroutine协程 和 channel管道
下一章:Chapter018 golang tcp socket编程快速入门
官方文档:https://studygolang.com/pkgdoc
一、反射的使用场景
1、结构体标签
2、函数的适配器
3、甚至可以自己开发go框架
…
二、基本介绍
1、反射可以在运行时动态获取变量的各种信息,比如变量的类型(type)、类别(kind)
2、如果是结构体变量,还可以获取结构体本身的信息(包括结构体的字段、方法)
3、通过反射,可以修改变量的值,可以调用关联方法
4、使用反射,需要import reflect
5、示意图
三、反射的相关函数
1、reflect.TypeOf,获取变量的类型,返回reflect.Type类型
2、reflect.ValueOf,获取变量的类型,返回reflect.ValueOf类型
3、变量、interface{}和reflect.Value是可以相互转换的
(1)将interface{}转换成reflect.Value
rval := reflect.ValueOf(b)
(2)将reflect.Value转换成interface{}
iVal := rVal.Interface()
(3)将interface{}转成原来的变量类型,使用类型断言
v := iVal.(Stu)
(4)转换关系图
四、反射快速入门
1、对基本数据类型
package main
import (
"fmt"
"reflect"
)
//演示反射 对基本数据类型
func reflectTest01(b interface{}) {
//通过反射获取到传入变量类型type、类别kind、值
//1、reflect.type 反射类型rTyp
rTyp := reflect.TypeOf(b)
fmt.Println("rType =",rTyp)
//2、 reflect.Value
rVal := reflect.ValueOf(b)
fmt.Println("rVal =",rVal)
fmt.Printf("rVal Type = %T\n",rVal)
//3、 下面将rVal转回interface{}
iV := rVal.Interface()
//4、将interface{} 通过断言再重新转成需要的类型
num2 :=iV.(int)
fmt.Println("num2 =",num2)
fmt.Printf("num2 Type = %T\n",num2)
}
func main() {
//基本数据类型
var num int = 100
reflectTest01(num)
}
2、对结构体
package main
import (
"fmt"
"reflect"
)
//演示反射 对结构体
type Student struct {
Name string
Age int
}
func reflectTest02(b interface{}) {
//1、reflect.type 反射类型rTyp
rTyp := reflect.TypeOf(b)
fmt.Println("rType =",rTyp)
//2、 reflect.Value
rVal := reflect.ValueOf(b)
fmt.Println("rVal =",rVal)
fmt.Printf("rVal Type = %T\n",rVal)
//3、 下面将rVal转回interface{}
iV := rVal.Interface()
fmt.Println("iV =",iV)
fmt.Printf("iV Type = %T\n",iV)
//4、将interface{} 通过断言再重新转成需要的类型才能取出结构体内容
stu,ok := iV.(Student)
if ok{
fmt.Println("stuName = ",stu.Name)
}
}
func main() {
//结构体
var stu Student
stu.Name = "tom"
stu.Age = 10
reflectTest02(stu)
}
运行结果:
3、通过set来修改变量要用指针
package main
import (
"fmt"
"reflect"
)
func reflectTest03(b interface{}) {
rVal := reflect.ValueOf(b)
fmt.Println("rVal kind = ",rVal)
rVal.Elem().SetInt(20)
}
func main() {
var num int = 100
//修改变量值
reflectTest03(&num)
fmt.Println(num)
}
五、反射的注意事项
1、reflect.ValueOf.Kind 或者 reflect.TypeOf.Kind返回常量
六、反射的应用
1、使用反射来调度结构体方法、字段、标签
(1)调度方法
(2)调度字段
(3)调度tag
(4)综合应用
package main
import (
"fmt"
"reflect"
)
type Monster struct {
Name string `json:"name"`
Age int `json:"age"`
Score float32 `json:"score"`
Sex string `json:"sex"`
Adress string
}
func (s Monster) Print() {
fmt.Println("--start--")
fmt.Println(s)
fmt.Println("--end--")
}
func (s Monster)GetSum(n1,n2 int) int {
return n1+n2
}
func (s Monster)Set(name string,age int,score float32,sex string) {
s.Name = name
s.Age = age
s.Score = score
s.Sex = sex
}
func TestStruct(a interface{}) {
//获取reflect.Type类型
typ := reflect.TypeOf(a)
//获取reflect.Value类型
val := reflect.ValueOf(a)
//获取a对应类别
kd := val.Kind()
//不是结构体就退出
if kd != reflect.Struct{
fmt.Println("except struct")
return
}
//获取到该结构体有多少字段
num := val.NumField()
fmt.Printf("this monster has %v 字段\n",num)
//结构体所有字段
for i:=0;i<num ;i++ {
//value.Field返回字段,但是不能运算,要运算要转换
fmt.Printf("Field %d : 值为%v\n",i,val.Field(i))
//获取标签,要用typ.Field
tagVal := typ.Field(i).Tag.Get("json")
if tagVal !=""{
fmt.Printf("Field %d:tag为%v\n",i,tagVal)
}
}
//返回方法数目
numOfMethed := val.NumMethod()
fmt.Printf("struct has %v metheds\n",numOfMethed)
//var params []reflect.Value
//获取到第二个方法(按照函数名ASCII字母排序)并调用它
val.Method(1).Call(nil)
//调用结构体第一个方法Method(0)
var params []reflect.Value
params = append(params,reflect.ValueOf(10))
params = append(params,reflect.ValueOf(40))
res := val.Method(0).Call(params) //传入参数[]reflect.Value
fmt.Println("res=",res[0].Int()) //返回结果,返回的结果是[]reflect.Value
}
func main() {
var a = Monster{
Name: "牛魔王",
Age: 400,
Score: 100.00,
Sex: "male",
Adress: "中国",
}
TestStruct(a)
}