06-反射使用
1、介绍
Go 语言实现了反射。我们一般用到的包是reflect
包,反射包中有两对非常重要的函数和类型,两个函数分别是:
方法名 | 描述 |
---|---|
reflect.TypeOf |
可以获得任意值的类型对象,返回类型:reflect.Type |
reflect.ValueOf |
可以获得任意值的值对象,返回类型:reflect.Value |
反射包中的所有方法基本都是围绕着 reflect.Type
和 reflect.Value
两个类型设计的。
我们通过 reflect.TypeOf
、reflect.ValueOf
可以将一个普通的变量转换成反射包中提供的 reflect.Type
和 reflect.Value
,随后就可以使用反射包中的方法对它们进行复杂的操作。
2、reflect.Type
类型 reflect.Type
是反射包定义的一个接口,接口中定义了一些方法,常用如下:
2.1、常用方法列表
方法名 | 描述 |
---|---|
Kind() Kind |
返回该变量的的具体分类 |
Name() string |
Name返回该类型在自身包内的类型名,如果是未命名类型会返回”” |
Implements(u Type) bool |
检查当前类型有没有实现接口 u |
Key() Type |
返回map 的key 的类型,不是map 会panic |
Field(i int) StructField |
根据索引,返回索引对应的结构体(struct )字段的信息。不是struct 会 panic ,i 越界也会panic |
NumField() int |
返回结构体(struct )成员字段数量。不是 struct 会 panic |
FieldByName(name string) (StructField, bool) |
根据给定字符串返回字符串对应的结构体字段的信息。 |
FieldByIndex(index []int) StructField |
多层成员访问时,根据 []int 提供的每个结构体的字段索引,返回字段的信息。 |
FieldByNameFunc(match func(string) bool) (StructField,bool) |
根据传入的匹配函数匹配需要的字段。 |
NumMethod() int |
返回该类型的方法集中方法的数目 |
Method(int) Method |
返回该类型方法集中的第i个方法,返回的是 Method |
MethodByName(name string) Value |
根据方法名返回该类型方法集中的方法,返回的是 Value |
类型与种类的区别:
- Type 是原生数据类型: int、string、bool、float32 ,以及 type 定义的类型,对应的反射获取方法是 reflect.Type 中 的
Name()
- 通过反射获取到的 数组, 切片, Map, 指针, 等类型的变量, 他们的
Type.Name()
都是返回的空
- 通过反射获取到的 数组, 切片, Map, 指针, 等类型的变量, 他们的
- Kind 是对象归属的品种:Int、Bool、Float32、Chan、String、Struct、Ptr(指针)、Map、Interface、Fune、Array、Slice、Unsafe Pointer等
2.2、使用示例
1 |
|
2.3、Kind 类型整理
1 |
|
3、reflect.Value
反射包中Value
的类型与Type
不同,它被声明成了结构体。这个结构体没有对外暴露的字段,但是提供了获取或者写入数据的方法。
3.1、常用方法列表
方法名 | 描述 |
---|---|
Call(in []Value) []Value |
调用方法,第一个参数是in[0] ,第二个是in[1] ,以此类推 |
Field(i int) StructField |
返回struct 类型的第 i 个字段的值,不是struct 会 panic ,i 越界也会panic 。 |
FieldByName(name string) Value |
根据字段名查找值,前提类型是struct |
Index(i int) Value |
返回第 i 个元素,主要用于遍历,不能越界。前提类型是 Array, Slice, String 之一, |
IsNil() bool |
判断返回值是否为nil 。如果值类型不是通道(channel )、函数、接口、map 、指针或 切片时发生panic ,类似于语言层的v== nil 操作。常被用于判断指针是否为空。 |
IsValid() bool |
返回v是否持有一个值。如果v 是Value 零值会返回假,此时v除了IsValid、String、Kind之外的方法都会导致panic 。常被用于判定返回值是否有效 |
MapIndex(key Value) Value |
根据索引获取对应的值,前提类型是map |
MapKeys() []Value |
返回map 中所有的key ,返回类型是切片slice |
Set(x Value) |
通过反射修改值。 |
Type() Type |
获取值的类型 |
Interface() interface {} |
将值以 interface{} 类型返回,可以通过类型断言转换为指定类型 |
Int() int64 |
将值以 int 类型返回,所有有符号整型均可以此方式返回 |
Uint() uint64 |
将值以 uint 类型返回,所有无符号整型均可以此方式返回 |
Float() float64 |
将值以双精度(float64)类型返回,所有浮点数(float32、float64)均可以此方式返回 |
Bool() bool |
将值以 bool 类型返回 |
Bytes() []bytes |
将值以字节数组 []bytes 类型返回 |
String() string |
将值以字符串类型返回 |
3.2、使用示例
1 |
|
1 |
|
输出:
1 |
|
4、实践场景
4.1、动态调用结构体方法、函数
使用 MethodByName("xx").Call(param)
调用函数
1 |
|
4.2、处理错误结果
1 |
|
4.3、解析结构体标签
使用field.Tag.Lookup()
或field.Tag.Get()
查找对应的标签值
1 |
|
4.4、判断是否实现接口
1 |
|
4.5、通过反射设置变量的值
想要在函数中通过反射修改变量的值,需要注意函数参数传递的是值拷贝,必须传递变量地址才能修改变量值。而 反射中使用专有的 Elem()
方法来获取指针对应的值。
1 |
|
06-反射使用
https://flepeng.github.io/021-Go-31-Go-基础-06-反射使用/