注:每天进步一点点,记录每日成长,思考的第06/365天 ;
本文预计阅读时间: 10分钟
1
01
反射定义
反射是指计算机程序在运行时(Run time)可以访问、检测和修改它本身状态或行为的一种能力。
维基百科
Go 语言中定义的反射,是编译时不确定这些变量的具体类型,在运行时检测和更新它们的值、调用它们的方法,这就是放射机制。
1
02
常用场景
1)编写一个函数时,参数类型是不确定的;
2)有时需要根据某些条件决定调用哪个函数,比如根据用户输入。
1
03
Go的反射reflect
Go的反射机制是由reflect包提供的,它定义了一个接口和一个结构体,即 reflect.Type
和reflect.Value
,它们提供很多函数来获取存储在接口里的类型信息。
并且 reflect 包提供了 reflect.TypeOf 和 reflect.ValueOf 两个函数来获取任意对象的
reflect.Type 和 reflect.Value。
func TypeOf (i interface {}) Type
func ValueOf (i interface {}) Value
1)TypeOf
调用TypeOf 函数,实参会转化为interface{ } 类型,然后实参的类型信息,方法集、值信息都会被存储到interface{ } 变量中,返回Type。
Type实际上是一个接口,它定了很多方法,用于获取类型相关的各种信息。
type Type interface {
//此类型的变量对齐后所占用的字节数
Align() int
// 如果是 struct 的字段,对齐后占用的字节数
FieldAlign() int
// 返回类型方法集里的第 `i` (传入的参数)个方法
Method(int) Method
// 通过名称获取方法
MethodByName(string) (Method, bool)
// 获取类型方法集里导出的方法个数
NumMethod() int
// 类型名称
Name() string
// 返回类型所在的路径,如:encoding/base64
PkgPath() string
// 返回类型的大小,和 unsafe.Sizeof 功能类似
Size() uintptr
// 返回类型的字符串表示形式
String() string
// 返回类型的类型值
Kind() Kind
// 类型是否实现了接口 u
Implements(u Type) bool
// 是否可以赋值给 u
AssignableTo(u Type) bool
// 是否可以类型转换成 u
ConvertibleTo(u Type) bool
// 类型是否可以比较
Comparable() bool
// 下面这些函数只有特定类型可以调用
// 如:Key, Elem 两个方法就只能是 Map 类型才能调用
// 类型所占据的位数
Bits() int
// 返回通道的方向,只能是 chan 类型调用
ChanDir() ChanDir
// 返回类型是否是可变参数,只能是 func 类型调用
// 比如 t 是类型 func(x int, y ... float64)
// 那么 t.IsVariadic() == true
IsVariadic() bool
// 返回内部子元素类型,只能由类型 Array, Chan, Map, Ptr, or Slice 调用
Elem() Type
// 返回结构体类型的第 i 个字段,只能是结构体类型调用
// 如果 i 超过了总字段数,就会 panic
Field(i int) StructField
// 返回嵌套的结构体的字段
FieldByIndex(index []int) StructField
// 通过字段名称获取字段
FieldByName(name string) (StructField, bool)
// FieldByNameFunc returns the struct field with a name
// 返回名称符合 func 函数的字段
FieldByNameFunc(match func(string) bool) (StructField, bool)
// 获取函数类型的第 i 个参数的类型
In(i int) Type
// 返回 map 的 key 类型,只能由类型 map 调用
Key() Type
// 返回 Array 的长度,只能由类型 Array 调用 Len() int
// 返回类型字段的数量,只能由类型 Struct 调用 NumField() int
// 返回函数类型的输入参数个数
NumIn() int
// 返回函数类型的返回值个数
NumOut() int
// 返回函数类型的第 i 个值的类型
Out(i int) Type复制
2)ValueOf
调用ValueOf函数,实参会转化为interface{ } 类型,存储的是实际变量,提供实际变量的各种信息,返回Value结构体。
Value 结构体定义了很多方法,通过这些方法可以直接操作 Value 字段 ptr 所指向的实际数据:
// 设置切片的 len 字段,如果类型不是切片,就会panic
func (v Value) SetLen(n int)
// 设置切片的 cap 字段
func (v Value) SetCap(n int)
// 设置字典的 kv
func (v Value) SetMapIndex(key, val Value)
// 返回切片、字符串、数组的索引 i 处的值
func (v Value) Index(i int) Value
// 根据名称获取结构体的内部字段值
func (v Value) FieldByName(name string) Value
.....复制
Value
字段还有很多其他的方法。例如:
// 用来获取 int 类型的值
func (v Value) Int() int64
// 用来获取结构体字段(成员)数量
func (v Value) NumField() int
// 尝试向通道发送数据(不会阻塞)
func (v Value) TrySend(x reflect.Value) bool
// 通过参数列表 in 调用 v 值所代表的函数(或方法
func (v Value) Call(in []Value) (r []Value)
// 调用变参长度可变的函数
func (v Value) CallSlice(in[]Value) []Value
.....复制
1
04
反射三大定律
1)反射将接口值转换为反射对象
type NewInt int
var x NewInt = 3
fmt.Println(reflect.ValueOf(x), reflect.TypeOf(x))复制
2) 反射可以将反射对象转换为接口对象
type NewInt int
var x NewInt = 3
y := reflect.ValueOf(x)
z := y.Interface()
var z NewInt = z.(NewInt)复制
3) 反射对象必须是可修改的
type NewInt int
var x NewInt = 3
//获取指针对应的反射对象 v (必须使用指针修改指向的值)
y := reflect.ValueOf(&x)
y = y.Elem()
y.SetInt(11)
fmt.Println(y.Interface())复制
1
05
Golang reflect慢主要有两个原因
1)涉及到内存分配以及后续的GC;
2)reflect实现里面有大量的枚举,也就是for循环,比如类型之类的。
例子:
// 通过接口来获取任意参数,然后一一揭晓
func DoFiledAndMethod(input interface{}) {
getType := reflect.TypeOf(input)
fmt.Println("get Type is :", getType.Name())
getValue := reflect.ValueOf(input)
fmt.Println("get all Fields is:", getValue)
// 获取方法字段
// 1. 先获取interface的reflect.Type,然后通过NumField进行遍历
// 2. 再通过reflect.Type的Field获取其Field
// 3. 最后通过Field的Interface()得到对应的value
for i := 0; i < getType.NumField(); i++ {
field := getType.Field(i)
value := getValue.Field(i).Interface()
fmt.Printf("%s: %v = %v\n", field.Name, field.Type, value)
}
// 获取方法
// 1. 先获取interface的reflect.Type,然后通过.NumMethod进行遍历
for i := 0; i < getType.NumMethod(); i++ {
m := getType.Method(i)
fmt.Printf("%s: %v\n", m.Name, m.Type)
}
}
// 运行结果:
get Type is : User
get all Fields is: {1 Allen.Wu 25}
Id: int = 1
Name: string = Allen.Wu
Age: int = 25
ReflectCallFunc: func(main.User)复制
1
06
反射的实际应用
IDE 中的代码自动补全功能、对象序列化(json 函数库)、fmt 相关函数的实现、ORM(全称是:Object Relational Mapping,对象关系映射)……