文章目录
- 前言
- [一、通过反射获取底层类型 reflect.typeOf()](#一、通过反射获取底层类型 reflect.typeOf())
- [二、反射获取底层的值 reflect.ValueOf()](#二、反射获取底层的值 reflect.ValueOf())
- 三、通过反射设置底层值
- [四 、进阶结构体反射示例](#四 、进阶结构体反射示例)
前言
反射就像是给程序装上了显微镜,运行时随时查看底层类型以及底层值,根据需要动态读写或调用方法。(看需求使用,反射非常消耗性能,可能在项目运行很长一段时间才会发现问题)
一、通过反射获取底层类型 reflect.typeOf()
go
package main
import (
"fmt",
"reflect"
)
type MyInt int //自定义类型
type Person struct { //结构体类型
Name string
Age int
}
//interface表示任何类型空接口
func reflectFn(x interface{}){
//通过reflcet.TypeOf获取底层类型
v:= reflect.TypeOf(x)
//v.Kind() //底层类型
//v.kind() //类型名称
fmt.println(v.Kind())
}
func main(){
a:=10
reflectFn(a) //int
b:=23.3
reflectFn(b) //float64
c:="Hello"
reflectFn(c) //string
d:= true
reflectFn(c) //bool
}
var e MyTnt = 34 //实例化自定义类型
reflectFn(e) //int 正常获取的是main.MyInt类型,Kind获取到底层类型Int
var f = Person {Name:"小明",Age:23} //实例化结构体
reflectFn(f) //ptr 正常应该获取到main.Person , Kind获取到的是ptr
var h = 23
reflectFn(&h) //传入的是指针 获取到的类型是 *int
var i =[3]{1,2,3} //数组类型
reflectFn(i) //array
var j =[]int{1,2,3}
reflectFn(j) //slice
二、反射获取底层的值 reflect.ValueOf()
go
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() + 10
fmt.Println(n)
break
case reflect.String:
s:=v.String()
fmt.println(s)
break
}
}
func main(){
var a =13
reflectValue(a)
}
三、通过反射设置底层值
go
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
}
四 、进阶结构体反射示例
go
package main
import (
"fmt"
"reflect"
)
type Person struct {
Name string
Age 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.Person
for 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:"[email protected]"} //合法的
}
for _,u := range users {
//User: {Name: Email:not-an-email}
//User: {Name:Alice Email:[email protected]}
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("校验通过 ✅")
}
}
}