目录
一、基本概念
在计算机科学领域,反射是指计算机程序在运行时(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 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 // 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
菜鸟实战,持续学习!