Go语言反射reflect深入理解

反射是指在程序运行期对程序本身进行访问和修改的能力。程序在编译时,变量被转换为内存地址,变量名不会被编译器写入到可执行部分。在运行程序时,程序无法获取自身的信息。

支持反射的语言可以在程序编译期将变量的反射信息,如字段名称、类型信息、结构体信息等整合到可执行文件中,并给程序提供接口访问反射信息,这样就可以在程序运行期获取类型的反射信息,并且有能力修改它们。

Go程序在运行期使用reflect包访问程序的反射信息

reflect包实现了运行时反射,允许程序操作任意类型的对象。典型用法是用静态类型interface{}保存一个值,通过调用TypeOf获取其动态类型信息,该函数返回一个Type类型值。调用ValueOf函数返回一个Value类型值,该值代表运行时的数据。Zero接受一个Type类型参数并返回一个代表该类型零值的Value类型值。

Go 程序的反射系统无法获取到一个可执行文件空间中或者是一个包中的所有类型信息,需要配合使用标准库中对应的词法、语法解析器和抽象语法树(AST)对源码进行扫描后获得这些信息。

通过反射获取类型信息

通过反射获取类型信息:(reflect.TypeOf()和reflect.Type)

使用 reflect.TypeOf() 函数可以获得任意值的类型对象(reflect.Type),程序通过类型对象可以访问任意值的类型信息。下面通过例子来理解获取类型对象的过程:

package main import ( "fmt" "reflect" ) type Student struct { Name string Age int } func main() { var stu Student typeOfStu := reflect.TypeOf(stu) fmt.Println(typeOfStu.Name(), typeOfStu.Kind()) }

代码输出如下:

Student struct

代码说明如下:

第16行,定义一个int类型的变量

第18行,通过reflect.TypeOf()取得变量stu的类型对象typeOfStu,类型为reflect.Type

第20行中,通过typeOfStu类型对象的成员函数,可以分别获取到 typeOfStu 变量的类型名为 Student,种类(Kind)为 struct。

理解反射的类型(Type)与种类(Kind)

在使用反射时,需要首先理解类型(Type)和种类(Kind)的区别。编程中,使用最多的是类型,但在反射中,当需要区分一个大品种的类型时,就会用到种类(Kind)。例如,需要统一判断类型中的指针时,使用种类(Kind)信息就较为方便。

反射种类(Kind)的定义

Go 程序中的类型(Type)指的是系统原生数据类型,如 int、string、bool、float32 等类型,以及使用 type 关键字定义的类型,这些类型的名称就是其类型本身的名称。例如使用 type A struct{} 定义结构体时,A 就是 struct{} 的类型。

种类(Kind)指的是对象归属的品种,在 reflect 包中有如下定义:

type Kind uint const ( Invalid Kind = iota // 非法类型 Bool // 布尔型 Int // 有符号整型 Int8 // 有符号8位整型 Int16 // 有符号16位整型 Int32 // 有符号32位整型 Int64 // 有符号64位整型 Uint // 无符号整型 Uint8 // 无符号8位整型 Uint16 // 无符号16位整型 Uint32 // 无符号32位整型 Uint64 // 无符号64位整型 Uintptr // 指针 Float32 // 单精度浮点数 Float64 // 双精度浮点数 Complex64 // 32位复数类型 Complex128 // 64位复数类型 Array // 数组 Chan // 通道 Func // 函数 Interface // 接口 Map // 映射 Ptr // 指针 Slice // 切片 String // 字符串 Struct // 结构体 UnsafePointer // 底层指针 )

Map、Slice、Chan 属于引用类型,使用起来类似于指针,但是在种类常量定义中仍然属于独立的种类,不属于 Ptr。

type A struct{} 定义的结构体属于 Struct 种类,*A 属于 Ptr。

从类型对象中获取类型名称和种类的例子

Go 语言中的类型名称对应的反射获取方法是 reflect.Type 中的 Name() 方法,返回表示类型名称的字符串。

类型归属的种类(Kind)使用的是 reflect.Type 中的 Kind() 方法,返回 reflect.Kind 类型的常量。

下面的代码中会对常量和结构体进行类型信息获取。

package main import ( "fmt" "reflect" ) //定义一个Enum类型 type Enum int const ( Zero Enum = 0 ) type Student struct { Name string Age int } func main() { //定义一个Student类型的变量 var stu Student //获取结构体实例的反射类型对象 typeOfStu := reflect.TypeOf(stu) //显示反射类型对象的名称和种类 fmt.Println(typeOfStu.Name(), typeOfStu.Kind()) //获取Zero常量的反射类型对象 typeOfZero := reflect.TypeOf(Zero) //显示反射类型对象的名称和种类 fmt.Println(typeOfZero.Name(), typeOfZero.Kind()) }

代码输出如下:

Student struct Enum int

代码说明如下:

第21行,将 Student 实例化,并且使用 reflect.TypeOf() 获取被实例化后的 Student 的反射类型对象。

第27行,输出Student的类型名称和种类,类型名称就是 Student,而 Student 属于一种结构体种类,因此种类为 struct。

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

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