目录
一:反射介绍
二:反射的type(常见于结构体获取结构体tag)
获取结构体tag示例
三:反射的value(常见于调用函数)
调用函数示例:
反射调用结构体方法示例
反射包中的所有方法基本都是围绕着 Type 和 Value 这两个类型设计的。 我们通过
reflect.TypeOf 、 reflect.ValueOf 可以将一个普通的变量转换成『反射』 包中提供的 Type 和 Value , 随后就可以使用反射包中的方法对它们进行复杂的操作。
通过反射得到结构体类型后,也可以针对结构体的某个字段进行type和value的反射。
结构体的type
结构体的type都是StructField结构;通过Field,FieldByIndex方法得到结构体某个成员的类型:StructField
StructField 的结构如下:
type StructField struct {
Name string // 字段名
PkgPath string // 字段路径
Type Type // 字段反射类型对象
Tag StructTag // 字段的结构体标签
Offset uintptr // 字段在结构体中的相对偏移
Index []int // Type.FieldByIndex中的返回的索引值
Anonymous bool // 是否为匿名字段
}
获取结构体type的方法:
Field(i int) StructField // 根据索引返回索引对应的结构体字段的信息,当值不是结构体或索引超界时发生宕机
// 返回结构体成员字段数量,当类型不是结构体或索引超界时发生宕机
NumField() int
//根据给定字符串返回字符串对应的结构体字段的信息,没有找到时 bool 返回 false,当类型不是结构体或索引超界时发生宕机
FieldByName(name string) (StructField, bool)
//用于结构体嵌套访问,根据 []int 提供的每个结构体的字段索引,返回字段的信息,没有找到时返回零值。当类型不是结构体或索引超界时发生宕机
FieldByIndex(index []int) StructField
//根据匹配函数匹配需要的字段,当值不是结构体或索引超界时发生宕机
FieldByNameFunc(match func(string) bool) (StructField,bool)
通过StructField 中 Tag structTag字段,又可以通过key获取对应的tag:
StructTag 拥有一些方法,可以进行 Tag 信息的解析和提取:
// 根据 Tag 中的键获取对应的值,例如`key1:"value1" key2:"value2"`的 Tag 中,可以传入“key1”获得“value1”。
func (tag StructTag) Get(key string) string
// 根据 Tag 中的键,查询值是否存在。
func (tag StructTag) Lookup(key string) (value string, ok bool)
package main
import (
"fmt"
"reflect"
)
func main() {
type cat struct {
Name string
Type int `json:"type" id:"100"`
}
// 通过reflect.TypeOf获取反射的type类型
typeOfCat := reflect.TypeOf(cat{})
// 通过type类型的FieldByName方法,获取字段名称为Tpye的结构体字段,其类型是StructField
if catType, ok := typeOfCat.FieldByName("Type"); ok {
// 结构体字段中的成员Tag通过Get方法,获取key为json的值,
// 也就是这里获取到了 Type int `json:"type" id:"100"` 中的"type",如果是根据id来获取,那么就是获取到100
fmt.Println(catType.Tag.Get("json"))
}
}
如果反射值对象(reflect.Value)中值的类型为函数时,可以通过 reflect.Value 调用该函数。
使用反射调用函数时,需要将参数使用反射值对象的切片 []reflect.Value 构造后传入 Call() 方法中,调用完成时,函数的返回值通过 []reflect.Value 返回。
package main
import (
"fmt"
"reflect"
)
// 普通函数
func add(a, b int) int {
return a + b
}
func main() {
// 将函数包装为反射值对象
funcValue := reflect.ValueOf(add)
// 构造函数参数, 传入两个整型值
paramList := []reflect.Value{reflect.ValueOf(10), reflect.ValueOf(20)}
// 反射调用函数
retList := funcValue.Call(paramList)
// 获取第一个返回值, 取整数值
fmt.Println(retList[0].Int())
}
调用结构体方法也是一样的
package main
import (
"fmt"
"reflect"
)
type User struct{
Id int
Name string
Age int
}
//设置Name方法
func (u *User) SetName(name string) string{
oldName := u.Name
u.Name = name
return oldName
}
//年龄数+1
func (u *User) AddAge() bool {
u.Age++
return true
}
//测试方法
func (u User) TestUser() {
fmt.Println("我只是输出某些内容而已....")
}
func main(){
//通过反射的方式调用结构体类型的方法
var setNameStr string = "SetName"
var addAgeStr string = "AddAge"
user := User{
Id : 1,
Name : "env107" ,
Age : 18 ,
}
//1.获取到结构体类型变量的反射类型
refUser:= reflect.ValueOf(&user) //需要传入指针,后面再解析
fmt.Println(refUser)
//2.获取确切的方法名
//带参数调用方式
//重点在这,通过MethodByName,通过字符串获取对应的结构体的方法,返回值是一个函数,然后调用过程和上面的例子的函数调用过程是一样的。
setNameMethod := refUser.MethodByName( setNameStr )
args := []reflect.Value{ reflect.ValueOf("Mike") } //构造一个类型为reflect.Value的切片
setNameMethod.Call(args) //返回Value类型
//不带参数调用方式
addAgeMethod := refUser.MethodByName( addAgeStr )
addAgeMethod.Call( make([]reflect.Value , 0) )
fmt.Println("User.Name = ",user.Name)
fmt.Println("User.Age = ",user.Age)
}