> 文档中心 > 【Go实战基础】反射是什么,如何用反射查看变量类型

【Go实战基础】反射是什么,如何用反射查看变量类型

目录

一、基本概念

二、数据结构

1、反射类型 Type

2、反射种类 Kind

3、反射值 Value

三、菜鸟实战

1、创建 g005.go

2、编译和运行

3、运行结果


一、基本概念

在计算机科学领域,反射是指计算机程序在运行时(runtime)可以访问、检测和修改它本身状态或行为的一种能力。用比喻来说,反射就是程序在运行的时候能够“观察”并且修改自己的行为。

每种语言的反射模型都不同,并且有些语言根本不支持反射。Go 语言实现了反射,反射机制就是在运行时动态的调用对象的方法和属性,官方自带的 reflect 包就是反射相关的,只要包含这个包就可以使用。

二、数据结构

reflect 实现了运行时的反射能力,能够让程序操作不同类型的对象,其中有两对非常重要的函数和类型,分别是:

reflect.TypeOf 能获取类型信息;
reflect.ValueOf 能获取数据的运行时表示;

1、反射类型 Type

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

类型 reflect.Type 是反射包定义的一个接口,我们可以使用 reflect.TypeOf 函数获取任意变量的类型,reflect.Type 接口中定义了一些有趣的方法。

// Type 接口type Type interface {    // 对应着unsafe的align,是一个计算大小的方法Align() int    // 字段大小,必须是type.kind=结构体类型FieldAlign() int    // 第 i 个方法Method(int) Method      // 根据名称获取方法MethodByName(string) (Method, bool)     // 方法的个数NumMethod() int      // 获取结构体名称Name() string     // 包路径PkgPath() string     // 占用内存的大小Size() uintptr      // 获取字符串表述String() string      // 数据类型Kind() Kind    // 判断是否实现了某接口Implements(u Type) bool     // 能否赋给另外一种类型AssignableTo(u Type) bool     // 能否转换为另外一种类型 ConvertibleTo(u Type) bool     // 解析指针(指针类型转为普通类型)Comparable() bool    // 类型所占据的位数Bits() int // 返回通道的方向ChanDir() ChanDir    // 返回类型是否是可变参数IsVariadic() bool    // 返回内部子元素类型Elem() Type      // 第i个成员Field(i int) StructField     // 根据index路径获取嵌套成员FieldByIndex(index []int) StructField     // 根据名称获取成员FieldByName(name string) (StructField, bool)    // 返回名称符合 func 函数的字段FieldByNameFunc(match func(string) bool) (StructField, bool)    // 获取函数类型的第 i 个参数的类型In(i int) Type    // 返回 map 的 key 类型Key() Type    // 容器的长度Len() int     // 返回类型字段的数量NumField() int    // 输出参数的个数NumIn() int    // 返回参数的个数NumOut() int     // 返回函数类型的第 i 个值的类型Out(i int) Type    // 返回类型结构体的相同部分common() *rtype    // 返回类型结构体的不同部分uncommon() *uncommonType}

2、反射种类 Kind

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

type Kind uintconst (    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     // 64位复数类型    Complex128    // 128位复数类型    Array  // 数组    Chan   // 通道    Func   // 函数    Interface     // 接口    Map    // 映射    Ptr    // 指针    Slice  // 切片    String // 字符串    Struct // 结构体    UnsafePointer // 底层指针)

3、反射值 Value

reflect.Value 是一个结构体类型,用于获取变量值的信息,可通过 reflect.ValueOf 函数获取修改原始数据类型(某个变量)的值信息。这个结构体没有对外暴露的字段,但是提供了获取或者写入数据的方法:

// Value 类型type Value struct {   // 代表的数据类型   typ *rtype   // 指向原始数据的指针   ptr unsafe.Pointer}// 一些方法,例如:// 对可寻址的值返回其地址func (v Value) Addr() Value// 将值以 bool 类型返回func (v Value) Bool() bool// 将值以字节数组 []bytes 类型返回func (v Value) Bytes() []byte// 将值以 interface{} 类型返回Interface() interface {}// 使用 int64 设置值Setlnt(x int64)...

三、菜鸟实战

实战需求:使用反射获取变量和方法信息

马上安排!

1、创建 g005.go

/* * @Author: 菜鸟实战 * @FilePath: /go110/go-005/g005.go * @Description: 反射 */ package main import ( "fmt" "reflect" "runtime" )  // typeOf 方法获取变量的信息 func reflect_type(a interface{}) {  // 获取 type t := reflect.TypeOf(a) fmt.Printf("a 的类型为: %v \n", t)  // 获取 kind k := t.Kind() switch k { case reflect.Int64: fmt.Printf("a 为 int64 \n") case reflect.String: fmt.Printf("a 为 string \n") default: fmt.Printf("a 未知 \n") }  }  // valueOf 方法获取变量的值信息 func reflect_value(a interface{}) {  // 获取 value v := reflect.ValueOf(a) // 获取 kind k := v.Kind()  switch k { case reflect.Int64: fmt.Printf("a 为 int64, 存储值为 %d \n", v.Int()) case reflect.String: fmt.Printf("a 为 string , 存储值为 %s \n", v.String()) default: fmt.Printf("a 未知 \n") }  }  // Field 利用发射获取结构体中的方法和调用 type Student struct { Name  string Age   int Score float32 }  func reflect_field() { // 创建结构体变量 var s Student = Student{ Name:  "Tom", Age:   10, Score: 80, }  v := reflect.ValueOf(s) t := v.Type() k := t.Kind()  // 分析 s 的变量类型 switch k { case reflect.Struct: fmt.Printf("s 为 struct \n") fmt.Printf("field num of s is : %d \n", v.NumField())  // field(i) 可以取得字段信息,返回一个 Value 类型的值 for i := 0; i < v.NumField(); i++ { field := v.Field(i) // 打印字段名称,类型 和 值 fmt.Printf("name : %s, type: %v, value : %v \n", t.Field(i).Name, field.Type().Kind(), field.Interface())  } default: fmt.Printf("s 不是 struct \n") }  }  // 主函数 func main() { // 使用内置函数打印 println("Hello", "菜鸟实战")  // type reflect_type(5) reflect_type("China")  // value reflect_value(10) reflect_value("golang")  // struct reflect_field()  // 使用包函数打印 fmt.Printf("版本: %s \n", runtime.Version()) } 

2、编译和运行

# 1、生成模块依赖
go mod init g005
 
# 2、编译
go build g005.go 
 
# 3、编译后的目录结构
 
└── go-005
    ├── g005
    ├── g005.go
    └── go.mod
 
# 4、运行
go run g005

3、运行结果

Hello 菜鸟实战
a 的类型为: int 
a 未知 
a 的类型为: string 
a 为 string 
a 未知 
a 为 string , 存储值为 golang 
s 为 struct 
field num of s is : 3 
name : Name, type: string, value : Tom 
name : Age, type: int, value : 10 
name : Score, type: float32, value : 80 
版本: go1.17.10 

菜鸟实战,持续学习!

开发者涨薪指南 【Go实战基础】反射是什么,如何用反射查看变量类型 48位大咖的思考法则、工作方式、逻辑体系