目录
一、概述
1、反射
- 反射可以在运行时动态获取变量的各种信息,比如变量的类型,类别
- 如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段、方法)
- 通过反射,可以修改变量的值,可以调用关联的方法
- 通过反射,需要import ("reflect")
import "reflect":reflect包实现了运行时反射,允许程序操作任意类型的对象。
type Type:Type类型用来表示一个go类型。
func ValueOf(i interface{}) Value:ValueOf返回一个初始化为i接口保管的具体值的Value,ValueOf(nil)返回Value零值。
- reflect.TypeOf(变量名):获取变量的类型,返回reflect.Type类型
- reflect.ValueOf(变量名):获取变量的值,返回reflect.Value类型 reflect.Value是一个结构体类型。
- 变量、interface{}和reflect.Value是可以相互转换的,这点在实际开发中,会经常使用到。
package main
import (
"fmt"
"reflect"
)
func reflectTest01(b interface{}){
//获取reflect.Type
rTyp := reflect.TypeOf(b)
fmt.Println("rTyp=",rTyp)
//获取reflect.Value
rVal := reflect.ValueOf(b)
fmt.Printf("rVal=%v rVal type=%T\n",rVal,rVal)
//将rVal转成interface{}
iv := rVal.Interface()
num :=iv.(int)
fmt.Println("num=",num)
}
func reflectTest02(b interface{}){
//获取reflect.Type
rTyp := reflect.TypeOf(b)
fmt.Println("rTyp=",rTyp)
//获取reflect.Value
rVal := reflect.ValueOf(b)
fmt.Printf("rVal=%v rVal type=%T\n",rVal,rVal)
//获取Kind
//1.
kind1 := rTyp.Kind()
//2.
kind2 :=rVal.Kind()
fmt.Printf("kind=%v kind=%v\n",kind1,kind2)
//将rVal转成interface{}
iv := rVal.Interface()
stu,ok :=iv.(Student)
if ok {
fmt.Println("stu=",stu)
}
}
type Student struct{
Name string
Age int
}
func main(){
var num int =100
reflectTest01(num)
stu :=Student{
Name:"小米",
Age:10,
}
reflectTest02(stu)
}
1.1、反射注意细节
- reflect.Value.Kind,获取变量的类别,返回的是一个常量
Kind代表Type类型值表示的具体分类。零值表示非法分类。
const (
Invalid Kind = iota
Bool
Int
Int8
Int16
Int32
Int64
Uint
Uint8
Uint16
Uint32
Uint64
Uintptr
Float32
Float64
Complex64
Complex128
Array
Chan
Func
Interface
Map
Ptr
Slice
String
Struct
UnsafePointer
)
- Type是类型,Kind是类别,Type和Kind可能是相同的,也可能是不同的。
- 比如:var num int =10 num的Type是int,Kind也是int
- 比如:var stu Student stu的Type是 包名.Student,Kind是struct
- 通过反射可以让变量在interface{}和Reflect.Value之间相互转换。
- 使用反射的方式来获取变量的值(并返回对应的类型),要求数据类型匹配,比如x是int,那么就应该使用reflect.Value(x).Int(),而不能使用其他的,否则报panic
- 通过反射来修改变量,注意当使用SetXxx方法来设置需要通过对应的指针类型来完成,这样才能改变传入的变量的值,同时需要使用到reflect.Value.Elem()方法。
package main
import (
"fmt"
"reflect"
)
func reflectTest01(b interface{}){
//获取reflect.Value
rVal := reflect.ValueOf(b)
fmt.Printf("rVal kind=%v\n",rVal.Kind())//rVal kind=ptr
//将rVal转成interface{}
rVal.Elem().SetInt(20)
}
func main(){
var num int =100
reflectTest01(&num)
fmt.Println("num",num)//num 20
}
- reflect.Value.Elem()应该如何理解?
func (v Value) Elem() Value:Elem返回v持有的接口保管的值的Value封装,
或者v持有的指针指向的值的Value封装。
package main
import (
"fmt"
"reflect"
)
func main(){
var num int =100
rVal := reflect.ValueOf(&num)
rVal.Elem().SetInt(200)
fmt.Printf("%v\n",num)
//rVal.Elem()用于获取指针指向变量,类似于
var num1=100
var b *int =&num1
*b=200
fmt.Printf("%v\n",num1)
}
1.2、案例
package main
import (
"fmt"
"reflect"
)
type Monster struct {
Name string `json:"name"`
Age int `json:"monster_age"`
Score float32
Sex string
}
func (s Monster) Print(){
fmt.Println("--start Print")
fmt.Println(s)
fmt.Println("--end")
}
func (s Monster) GetSum(n1,n2 int) int{
return n1+n2
}
func reflectTest01(b interface{}){
//获取reflect.Type
rTyp := reflect.TypeOf(b)
//获取reflect.Value
rVal := reflect.ValueOf(b)
kd :=rVal.Kind()
if kd != reflect.Struct {
fmt.Println("not struct")
return
}
num := rVal.NumField()
fmt.Printf("struct has %d fields\n",num)
//遍历结构体的所有字段
for i:=0;i<num;i++{
fmt.Printf("field %d: 值为=%v\n",i,rVal.Field(i))
tagVal := rTyp.Field(i).Tag.Get("json")
//有标签就显示,无,不显示
if tagVal != ""{
fmt.Printf("field %d: tag为=%v\n",i,tagVal)
}
}
numMethod := rVal.NumMethod()
fmt.Printf("struct has %d Methods\n",numMethod)
//方法的排序默认是按照 函数名的排序(ASCII码的大小排序)
rVal.Method(1).Call(nil)//获取第二个方法,调用它
//调用结构体的第一个方法Method(0)
var params []reflect.Value
params=append(params,reflect.ValueOf(10))
params=append(params,reflect.ValueOf(40))
res := rVal.Method(0).Call(params)//传入参数是[]reflect.Value
fmt.Println("res=",res[0].Int())//返回结果,返回的结果是 []reflect.Value
}
func main(){
var b Monster=Monster{
Name:"小明",
Age:40,
Score:30.0,
Sex:"男",
}
reflectTest01(b)
}
干我们这行,啥时候懈怠,就意味着长进的停止,长进的停止就意味着被淘汰,只能往前冲,直到凤凰涅槃的一天!