golang中的反射示例

文章目录

  • 前言
  • [一、通过反射获取底层类型 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("校验通过 ✅")
		} 
 }


}
相关推荐
Chandler244 小时前
Go语言 GORM框架 使用指南
开发语言·后端·golang·orm
wktomo5 小时前
GO语言学习(二)
学习·golang
你怎么知道我是队长5 小时前
Go语言语法---输入控制
golang
蚂蚁在飞-5 小时前
Golang基础知识—cond
开发语言·后端·golang
李迟6 小时前
Golang实践录:在go中使用curl实现https请求
开发语言·golang·https
BUG制造机.6 小时前
Go 语言的 GMP 模型
golang
张帅涛_6667 小时前
golang读、写、复制、创建目录、删除、重命名,文件方法总结
golang
运维-大白同学7 小时前
go-数据库基本操作
开发语言·数据库·golang
你怎么知道我是队长7 小时前
GO语言语法---if语句
golang