golang中的反射示例
文章目录
- 前言
- 一、通过反射获取底层类型 reflect.typeOf()
- 二、反射获取底层的值 reflect.ValueOf()
- 三、通过反射设置底层值
- 四 、进阶结构体反射示例
前言
反射就像是给程序装上了显微镜,运行时随时查看底层类型以及底层值,根据需要动态读写或调用方法。(看需求使用,反射非常消耗性能,可能在项目运行很长一段时间才会发现问题)
一、通过反射获取底层类型 reflect.typeOf()
package mainimport ("fmt","reflect"
)type MyInt int //自定义类型type Person struct { //结构体类型Name stringAge int
}//interface表示任何类型空接口
func reflectFn(x interface{}){//通过reflcet.TypeOf获取底层类型v:= reflect.TypeOf(x)//v.Kind() //底层类型//v.kind() //类型名称fmt.println(v.Kind())
}func main(){a:=10reflectFn(a) //intb:=23.3reflectFn(b) //float64c:="Hello"reflectFn(c) //stringd:= truereflectFn(c) //bool
}var e MyTnt = 34 //实例化自定义类型
reflectFn(e) //int 正常获取的是main.MyInt类型,Kind获取到底层类型Intvar f = Person {Name:"小明",Age:23} //实例化结构体
reflectFn(f) //ptr 正常应该获取到main.Person , Kind获取到的是ptrvar h = 23
reflectFn(&h) //传入的是指针 获取到的类型是 *intvar i =[3]{1,2,3} //数组类型
reflectFn(i) //arrayvar j =[]int{1,2,3}
reflectFn(j) //slice
二、反射获取底层的值 reflect.ValueOf()
package main
import ("fmt""reflect"
)//传入x 类型为空接口类型 任意类型
func reflectValue(x,interface{}){/* 10 + x (mismatched types int and interface{}) 类型不一致 */ var num = 10 + x /*使用类型断言 断言的类型就是()中的类型,,失败为缺省值,成功就是x的值。1.适用于已知的数据类型。2.比较常用的也是类型断言,使用反射有性能开销*/b,_ = x.(int) var num = 10 + b // 23/* 已知原始值类型 */v:=reflect.ValueOf(x) //获取原始值var n = v.Int()+10 //类型转换 23/* 未知原始值类型 */v:=reflect.ValueOf(x) kind := v.Kind() 获取原始类型switch kind{ //判断原始类型执行不同的操作 case reflect.Int:n:=v.Int() + 10fmt.Println(n) breakcase reflect.String:s:=v.String()fmt.println(s)break}}func main(){var a =13reflectValue(a)
}
三、通过反射设置底层值
package main func reflectSetValue(x interface{}){
/*方案一:使用类型断言*/
v := x.(*int64) //使用类型断言
*v = 120 //修改原始值 通过 & *已经获取到内存地址,通过*v 则可以修改底层值 /*方案二 使用反射*/
v:= reflect.ValueOf(x)//1. v.Elem获取指针地址的值 // 2. kind获取底层值的类型
if v.Elem().Kind() ==reflect.Int64{v.Elem().SetInt(120) //修改底层值 }
}func main(){
var int64 = 10
reflectSetValue(&a) //传入指针 如果要修改值必须要传入指针
fmt.Prinyln(a) //打印120
}
四 、进阶结构体反射示例
package main
import ("fmt""reflect"
)type Person struct {Name stringAge int
}// User 定义了两个字段:
// Name 必填
// Email 必填且必须符合邮箱格式
type User Struct {Name string `json:"name" validate:"required"` // validate:"required" 表示 Name 字段不能为空Email string `json:"email" validate:"required,Email"` // validate:"required,email" 表示 Email 字段不能为空且必须符合邮箱格式
}func main(){/*实例一: 遍历所有的字段*/p := Person("Alice",23) //实例话Person结构体/*情况一: 只遍历 */t:=reflect.TypeOf(p) //main.Personfor i:=0; i< t.NumFiled(); i++{fmt.println(t.filed(i).Name) // Name / Age}/*情况二: 获取值*/v:= reflect.ValueOf(p) for i:=0; i< v.numFiled(); i++{//使用interface转为具体go值 类型为interface,可以通过断言具体类型//不使用interface是返回一个反射值的对象fmt.println(v.filed(i).interface()) // Alice / 23}/*情况三 修改值 */v := reflect.ValueOf(&p).Elem() //修改传入指针 要使用Elem拿到底层数据ageFiled :=v.FieldByName("Age") // 打印23 得到的其实反射的对象 拿到名为 Age 的字段if(ageFiled.CanSet() && ageFiled.Kind() == reflect.Int){ //CanSet是否为可修改的,Kind底层数据类型是否为Int ageField.SetInt(43) //SetInt修改底层数据 } /*实例二: 结构体tag,场景类似于前端传过来的数据,验证Name Email格式是否正确*/validate := validate.New() //创建验证器实例users := []User{ //实例化结构体{Name:"" ,Email:"not-an-email"}, //不合法的{Name:"Alice",Email:"alice@example.com"} //合法的}for _,u := range users {//User: {Name: Email:not-an-email}//User: {Name:Alice Email:alice@example.com}fmt.printf("效验 User: %+v\n" ,u) err := validate.Struct(u) //调用Struct方法执行整体效验,根据struct标签驱动规则if err != nil { //err 非 nil ,表示有字段效验失败了,否则所有字段通过 // 将err转为ValidationErrors ,遍历每个字段的错误for _,fielfErr := range err.(validator.ValidationErrors){// fieldErr.Field() —— 失败字段名// fieldErr.Tag() —— 触发失败的规则// fieldErr.Value() —— 该字段的实际值fmt.Printf("字段 %q 校验失败:tag=%q, 值=%v\n",fieldErr.Field(),fieldErr.Tag(),fieldErr.Value(),)}}else{// 当所有字段通过校验时打印fmt.Println("校验通过 ✅")} }}