文章目录

  • ​​反射介绍​​
  • ​​reflect包​​
  • ​​结构体反射​​

反射介绍

反射可以再运行时动态获取变量信息,并修改变量内容。是实现动态编程、编写框架的关键。

Go程序在运行期使用reflect包访问程序的反射信息

reflect包

go语言的反射机制中,任何借口都是由一个具体类型(reflect.Type)和具体类型的值(reflect.Value)两部分组成。reflect包还提供了 reflect.TypeOf()和 reflect.Valueof两个函数。来获取任意对象的Value 和 Type
示例代码

func typeValueTest(){
x := 10
t := reflect.TypeOf(x)
v := reflect.ValueOf(x)

fmt.Printf("type:=%v value=%v \n",t,v)//type:=int value=10
}

反射中类型分为两种:Type 和Kind ,go 中我们可以使用type 关键字构造很多自定义类型,king是指的底层的类型,当需要区分指针结构体等大品种类型时使用Kind.
示例代码

func test01(){
type myInt int64
var a *float32 //指针
var b myInt //自定义类型
var c rune //类型别名

t := reflect.TypeOf(a)
//type:= kind=ptr
fmt.Printf("type:=%v kind=%v \n",(),t.Kind())

t = reflect.TypeOf(b)
//type:=myInt kind=int64
fmt.Printf("type:=%v kind=%v \n",(),t.Kind())

t = reflect.TypeOf(c)
//type:=int32 kind=int32
fmt.Printf("type:=%v kind=%v \n",(),t.Kind())

type person struct{
name string
age int
}
type book struct{
title string
}
var d = person{
name:"tom",
age:19,
}
var e = book{title:"booktitle",}
t = reflect.TypeOf(d)
//type:=person kind=struct
fmt.Printf("type:=%v kind=%v \n",(),t.Kind())

t = reflect.TypeOf(e)
//type:=book kind=struct
fmt.Printf("type:=%v kind=%v \n",(),t.Kind())

}

go中数组 切片 map 指针等类型的变量,他们的类型的Name方法都返回空,
go 中定义的Kind 类型清单如下:

type Kind uint
const (
Invalid Kind = iota // 非法类型
Bool // 布尔型
Int // 有符号整型
Int8 // 有符号8位整型
Int16 // 有符号16位整型
Int32 // 有符号32位整型
Int64 // 有符号64位整型
Uint // 无符号整型
Uint8 // 无符号8位整型
Uint16 // 无符号16位整型
Uint32 // 无符号32位整型
Uint64 // 无符号64位整型
Uintptr // 指针
Float32 // 单精度浮点数
Float64 // 双精度浮点数
Complex64 // 64位复数类型
Complex128 // 128位复数类型
Array // 数组
Chan // 通道
Func // 函数
Interface // 接口
Map // 映射
Ptr // 指针
Slice // 切片
String // 字符串
Struct // 结构体
UnsafePointer // 底层指针
)

反射中的reflect.Value类型中包含了原始值的信息,reflect.Value 和原始值可以互换。reflect.Value 提供的方法如下:

Interface() interface {} 将值以 interface{} 类型返回,可以通过类型断言转换为指定类型
Int() int64 将值以 int 类型返回,所有有符号整型均可以此方式返回
Uint() uint64 将值以 uint 类型返回,所有无符号整型均可以此方式返回
Float() float64 将值以双精度(float64)类型返回,所有浮点数(float32、float64)均可以此方式返回
Bool() bool 将值以 bool 类型返回
Bytes() []bytes 将值以字节数组 []bytes 类型返回
String() string 将值以字符串类型返回
获取变量值示例

func test02(){
var a float32 = 3.12
var b int64 = 123
reflectValue(a)//type is Float32 value = 3.120000
reflectValue(b)//type is int64 value = 123
c := reflect.ValueOf(10)
fmt.Printf("type is %T \n",c)//type is reflect.Value
}
func reflectValue(x interface{}){
v := reflect.ValueOf(x)
k :=v.Kind();
switch k{
case reflect.Int64:
//() 获取到原始值,然后强转成 int 64
fmt.Printf("type is int64 value = %d \n",int64(()))
case reflect.Float64:
//() 获取到原始值,然后强转
fmt.Printf("type is Flaot64 value = %f \n",float64(v.Float()))
case reflect.Float32:
//() 获取到原始值,然后强转
fmt.Printf("type is Float32 value = %f \n",float32(v.Float()))
}
}

设置变量值
想要在函数中通过反射修改变量值,必须传地址。反射中使用 Elem()来获取指针对应的值

func test03(){
var a int64 = 100
//setValue1(a)//报异常
//fmt.Println(a)
setValue2(&a)//必须传指针才能赋值
fmt.Println(a)//200
var bb int =10
//v := reflect.ValueOf(bb)
//v.SetInt(20)//panic: reflect: reflect.Value.SetInt using unaddressable value

v := reflect.ValueOf(&bb)//这样修改值才正确
v.Elem().SetInt(20)
fmt.Println(bb)//20
}
func setValue1(x interface{}){
v := reflect.ValueOf(x)
if(v.Kind() == reflect.Int64){
v.SetInt(200)//panic: reflect: reflect.Value.SetInt using unaddressable value
}
}
func setValue2(x interface{}){
v := reflect.ValueOf(x)
if(v.Elem().Kind() == reflect.Int64){
v.Elem().SetInt(200)
}
}

reflect.Value 的 IsNil 方法:判断当前value持有的值是否为nil,value 只有的值必须是通道、函数、接口、映射、指针、切片之一,否则报错。常被用于判断指针是否为空
reflect.Value 的 IsValid 方法:判断当前Vlaue 是否持有一个值,常被用于判断返回值是否有效

func test04(){
var a *int
//var a *int is NIl true
fmt.Println("var a *int is NIl",reflect.ValueOf(a).IsNil())
//Nil isValid false
fmt.Println("Nil isValid ",reflect.ValueOf(nil).IsValid())
b := struct{}{}
//不存在的结构体成员: false
fmt.Println("不存在的结构体成员:",reflect.ValueOf(b).FieldByName("abc").IsValid())
//不存在的结构体方法: false
fmt.Println("不存在的结构体方法:",reflect.ValueOf(b).MethodByName("abc").IsValid())
c:=map[string]int{}
//map中不存在的键: false
fmt.Println("map中不存在的键:",reflect.ValueOf(c).MapIndex(reflect.ValueOf("nazha")).IsValid())
}

结构体反射

相关方法
结构体对象的 reflect.Type对象可以通过 NumFiled() 和 Filed()方法获得结构体成员的详细信息
reflect.Type 中与结构体相关的方法:
Field(i int) StructField 根据索引,返回索引对应的结构体字段的信息。
NumField() int 返回结构体成员字段数量。
FieldByName(name string) (StructField, bool) 根据给定字符串返回字符串对应的结构体字段的信息。
FieldByIndex(index []int) StructField 多层成员访问时,根据 []int 提供的每个结构体的字段索引,返回字段的信息。
FieldByNameFunc(match func(string) bool) (StructField,bool) 根据传入的匹配函数匹配需要的字段。
NumMethod() int 返回该类型的方法集中方法的数目
Method(int) Method 返回该类型方法集中的第i个方法
MethodByName(string)(Method, bool) 根据方法名返回该类型方法集中的方法

StructField
用来描述结构体中的一个字段的信息,定义如下:

type StructField struct {
// Name是字段的名字。PkgPath是非导出字段的包路径,对导出字段该字段为""。
// 参见http:///ref/spec#Uniqueness_of_identifiers
Name string
PkgPath string
Type Type // 字段的类型
Tag StructTag // 字段的标签
Offset uintptr // 字段在结构体中的字节偏移量
Index []int // 用于Type.FieldByIndex时的索引切片
Anonymous bool // 是否匿名字段
}

示例代码

```css
type student struct{
Name string `json:"name"`
Score int `json:"score"`
}
func(s student) Sleep(){
fmt.Println("shuijiao。。。。")
}
func(s student) Study(){
fmt.Println("study.....")
}
func test05(){
stu1:=student{
Name:"tom",
Score:91,
}
t:=reflect.TypeOf(stu1)
fmt.Println((),t.Kind())
for i:=0;i<t.NumField();i++{
field:=t.Field(i)
fmt.Printf("name:%s,index:%d,type:%v,json tag:%v \n",,field.Index,field.Type,field.Tag.Get("json"))
}

v:=reflect.ValueOf(t)

for i:=0;i<v.NumMethod();i++{
methodType:=v.Method(i).Type
fmt.Printf("method name :%s type:%s \n",t.Method(i).Name,methodType)
//var args = []reflect.Value{}
v.Method(i).Call(nil)//调用方法
}
}