Go语言反射(reflect)及应用

Go语言反射(reflect)及应用 基本原理及应用场景

在编译时不知道类型的情况下,可更新变量、在运行时查看值、调用方法以及直接对它们的布局进行操作,这种机制被称为反射

具体的应用场景大概如下:

动态地获取变量的各种信息(包括变量的类型type、类别kind);

如果是结构体变量,还可以获取结构体本身的字段、方法;

可以修改变量的值,调用变量的方法;

具体应用场景:

编写函数的适配器;

func funcName(funcPtr interface{},args ...interface{}){}

在暂时未知调用哪个接口的时候,进行传参,传入的是可变参数args,这时候配合传入的函数指针funcPtr,利用反射,进行动态地调用函数。

func testInt(b interface{}) { //获取类型 rType := reflect.TypeOf(b) fmt.Println("rType:",rType) //获取值 rVal := reflect.ValueOf(b) n := rVal.Int() fmt.Printf("rVal value:%v , type: %T\n",rVal,rVal) fmt.Printf("n value: %v , type: %T \n",n,n) //获取interface{} Ir := rVal.Interface() fmt.Printf("Ir , value: %v , type: %T \n",Ir,Ir) //类型断言 num := Ir.(int) fmt.Printf("num , value: %v , type: %T \n",num,num) } func testStruct(b interface{}) { rType := reflect.TypeOf(b) fmt.Println("rType:",rType) //获取值 rVal := reflect.ValueOf(b) fmt.Printf("rVal value:%v , type: %T\n",rVal,rVal) //获取interface{} Ir := rVal.Interface() fmt.Printf("Ir , value: %v , type: %T \n",Ir,Ir) rKind := rVal.Kind() //表示数据类别 fmt.Printf("rkind , kind: %v , type: %T \n",rKind,rKind) //类型断言 num ,ok:= Ir.(Student) if ok { fmt.Printf("num , value: %v , type: %T \n", num, num) fmt.Println(num.Name) } }

对结构体进行序列化,需要制定Tag。

在对函数结构体序列化的时候,自定义Tag用到了反射,生成相对应的字符串。

reflect 包及相关常用函数 type type Kind uint

Kind代表Type类型值表示的具体分类。零值表示非法分类。

type type Type interface { ... }

Type类型用来表示一个go类型。

不是所有go类型的Type值都能使用所有方法。请参见每个方法的文档获取使用限制。在调用有分类限定的方法时,应先使用Kind方法获知类型的分类。调用该分类不支持的方法会导致运行时的panic。

func func TypeOf(i interface{}) Type

TypeOf返回接口中保存的值的类型,TypeOf(nil)会返回nil。

type type Value struct { // 内含隐藏或非导出字段 }

Value为go值提供了反射接口。

不是所有go类型值的Value表示都能使用所有方法。请参见每个方法的文档获取使用限制。在调用有分类限定的方法时,应先使用Kind方法获知该值的分类。调用该分类不支持的方法会导致运行时的panic。

Value类型的零值表示不持有某个值。零值的IsValid方法返回false,其Kind方法返回Invalid,而String方法返回"",所有其它方法都会panic。绝大多数函数和方法都永远不返回Value零值。如果某个函数/方法返回了非法的Value,它的文档必须显式的说明具体情况。

如果某个go类型值可以安全的用于多线程并发操作,它的Value表示也可以安全的用于并发。

func func ValueOf(i interface{}) Value

ValueOf返回一个初始化为i接口保管的具体值的Value,ValueOf(nil)返回Value零值。

func (Value) func (v Value) Kind() Kind

Kind返回v持有的值的分类,如果v是Value零值,返回值为Invalid

func (Value) func (v Value) Elem() Value

Elem返回v持有的接口保管的值的Value封装,或者v持有的指针指向的值的Value封装。如果v的Kind不是Interface或Ptr会panic;如果v持有的值为nil,会返回Value零值。

unc (Value) func (v Value) NumField() int

返回v持有的结构体类型值的字段数,如果v的Kind不是Struct会panic

func (Value) func (v Value) Field(i int) Value

返回结构体的第i个字段(的Value封装)。如果v的Kind不是Struct或i出界会panic

func (Value) func (v Value) NumMethod() int

返回v持有值的方法集的方法数目。

func (Value) func (v Value) Method(i int) Value

返回v持有值类型的第i个方法的已绑定(到v的持有值的)状态的函数形式的Value封装。返回值调用Call方法时不应包含接收者;返回值持有的函数总是使用v的持有者作为接收者(即第一个参数)。如果i出界,或者v的持有值是接口类型的零值(nil),会panic。

func (Value) func (v Value) MethodByName(name string) Value

返回v的名为name的方法的已绑定(到v的持有值的)状态的函数形式的Value封装。返回值调用Call方法时不应包含接收者;返回值持有的函数总是使用v的持有者作为接收者(即第一个参数)。如果未找到该方法,会返回一个Value零值。

更多其它类型以及函数:Go语言标准库文档。

注意事项及细节

变量、interface{} 和 reflect.Value 是可以相互转换的。

image-20201103190247769

reflect.Value.Kind,获取变量的类别,返回的是一个常量

Type 和 Kind 的区别

Type 是类型, Kind是类别, Type 和Kind 可能是相同的,也可能是不同的。

比如: var num int = 10, num 的 Type 是 int , Kind 也是 int;

比如: var stu Student stu 的Type 是 packageXXX.Student , Kind 是 struct 。

通过反射的来修改变量, 注意当使用SetXxx 方法来设置,需要通过传入对应的指针类型来完成, 这样才能改变传入的变量的值;

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wspgsf.html