Go中的反射
反射就是程序在运行时,检查变量存储的数据,进而获得其数据类型
对于静态类型为接口的变量,其动态类型可以是实现了接口方法的任何数据
Go是强类型语言,由于不知道其动态类型的数据类型,也就无法对其进行操作,此时我们需要使用反射来获取其动态类型的数据类型
Go的官方reflect包实现了运行时反射,但是需要注意的是反射的性能较差,过多的使用反射会导致程序性能受影响
reflect包中的类型
type Type
表示一个数据类型,并非所有类型的Type都能使用所有方法,调用该类型不支持的方法会导致运行时的panic
type Type interface { // 基本信息 Align() int // 当从内存中申请一个该类型值时,会对齐的字节数 FieldAlign() int // 当该类型作为结构体的字段时,会对齐的字节数 Name() string // 该类型在自身包内的类型名,未命名类型返回"" PkgPath() string // 返回定义该类型的包路径,如"encoding/base64",内建类型或未命名类型返回"" Size() uintptr // 要保存一个该类型的值需要多少字节,类似unsafe.Sizeof String() string // 类型的字符串表示,可能会使用短包名(如base64代替"encoding/base64") // 如果比较两个类型是否相等,应该用Type类型比较 Kind() Kind // 该接口的基础类型 Comparable() bool // 值是否具有可比性,即使返回true仍然可能palic,如接口是可比的,但是其动态类型可能不可比 CanSeq() bool // 是否可迭代(最多每次迭代可赋值1个值) CanSeq2() bool // 是否可迭代(最多每次迭代可赋值2个值) // 基础类型 Bits() int // 字位数。如果Kind不是Int、Uint、Float或Complex,会panic ChanDir() ChanDir // 一个channel的方向,非通道类型会panic Key() Type // map键的类型,非映射类型将panic Len() int // array的长度,非数组类型将panic OverflowComplex(x complex128) bool // x使用该类型表示时会溢出,Kind不是Complex64、Complex128会panic OverflowFloat(x float64) bool // ...,Kind不是Float32、Float64会panic OverflowInt(x int64) bool // ...,Kind不是Int、Int8、Int16、Int32、Int64会panic OverflowUint(x uint64) bool // ...,Kind不是Uint、Uintptr、Uint8、Uint16、Uint32、Uint64会panic // 函数类型 IsVariadic() bool // 函数最后一个输入参数是否为"..."形式 In(i int) Type // func的第i个参数的类型 NumIn() int // func的参数个数 NumOut() int // func的返回值个数 Out(i int) Type // func的第i个返回值的类型 // 结构体 NumField() int // struct的字段数(所有匿名字段算作一个字段) Field(i int) StructField // struct的第i个字段的类型 FieldByIndex(index []int) StructField // 按照索引获得嵌套字段的类型 // 用于获取结构体中成员仍然为结构体时子成员结构体的字段类型 // 等价于用索引中每个值链式调用本方法 FieldByName(name string) (StructField, bool) // 获取类型名为name的字段(会查找匿名字段及其子字段) // 布尔值说明是否找到 FieldByNameFunc(match func(string) bool) (StructField, bool) // 第一个满足函数match的字段 // 布尔值说明是否找到 // 接口的方法 // 非结构体具体实现的方法,因为结构体可能在实现了接口方法之外还有其他方法 NumMethod() int // 该类型所有方法的总数,匿名字段的方法会被计算 Method(int) Method // 方法集中的第i个方法,i不在[0, NumMethod())范围内时导致panic // 接口类型返回值的Type字段描述方法的签名,Func字段为nil // 非接口类型返回值的Type字段和Func字段描述方法的未绑定函数状态 MethodByName(string) (Method, bool) // 根据方法名返回方法集中的方法,布尔值表示是否发现该方法 // 接口判断 Implements(u Type) bool // 是否实现了u接口 AssignableTo(u Type) bool // 是否可以赋值给u接口 ConvertibleTo(u Type) bool // 是否可以转换为u接口的类型 // 其他 Elem() Type // 元素的类型,Kind不是Array、Chan、Map、Ptr或Slice,会panic }
创建一个Type,即获取一个接口中保存值的Type
获取Type TypeOf(i any) Type :获取接口中保存值的类型,TypeOf(nil)会返回nil TypeFor[T any]() Type :返回类型参数 T 的 Type 创建Type ArrayOf(length int, elem Type) Type :返回具有给定长度和元素类型的数组Type ChanOf(dir ChanDir, t Type) Type :返回具有给定方向和元素类型的通道Type FuncOf(in, out []Type, variadic bool) Type :返回具有给定参数和返回类型的函数Type,variadic表示是否为不定参数函数 MapOf(key, elem Type) Type :返回具有给定键和元素类型的映射Type PointerTo(t Type) Type :返回指向 t 类型的指针Type SliceOf(t Type) Type :返回元素类型为 t 的切片Type StructOf(fields []StructField) Type :返回包含指定字段的结构体Type
type Value
表示一个数据中存储的值,可用用来进行获取和修改等操作
type Value struct {} // 没有导出字段
创建Value
获取Value ValueOf(i interface{}) Value :获取i持有值的Value,nil返回Value零值 修改并返回新的Value Append(s Value, x ...Value) Value:向切片类型的Value值s中添加一系列值,x持有的值必须能直接赋值给s持有的切片的元素类型 AppendSlice(s, t Value) Value :类似Append函数,但接受一个切片类型的Value值,将切片t的每一个值添加到s 创建新的Value Zero(typ Type) Value :返回持有typ零值的Value,注意持有零值和零值是不同的 New(typ Type) Value :返回持有指向类型为typ的新申请的零值指针,返回值为PointerTo(typ) NewAt(typ Type, p unsafe.Pointer) Value:返回持有指向类型typ、地址为p的值的指针 Indirect(v Value) Value :返回v的指针指向值的Value。v持有nil指针会返回Value零值,v不持有指针返回v MakeSlice(typ Type, len, cap int) Value:创建一个新申请的元素类型为typ,长度len容量cap的切片类型的Value MakeMap(typ Type) Value :创建一个特定映射类型的Value MakeMapWithSize(typ Type, n int) Value :使用 n 个指定类型的元素和初始空间创建一个新映射的Value值 MakeChan(typ Type, buffer int) Value:创建一个元素类型为typ、有buffer个缓存的通道类型的Value MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value 创建一个具有给定类型、包装函数fn的函数的Value,当被调用时会运行fn(args...) 函数fn的实现可以假设参数Value切片匹配typ类型指定的参数数目和类型 如果typ表示一个可变参数函数类型,参数切片中最后一个Value本身必须是一个包含所有可变参数的切片 fn返回的结果Value切片也必须匹配typ类型指定的结果数目和类型
Value的方法很多,用来操作不同数据类型的值
基本信息
判断相关 IsValid() bool :是否持有一个值,零值会返回false IsNil() bool :持有值是否为nil,持有值必须是通道、函数、接口、映射、指针、切片 IsZero() bool :是否为零值 CanAddr() bool :是否可以获取持有值的指针(可寻址) CanSet() bool :持有值是否可修改,只有当一个值可寻址同时又不是非导出字段时才可修改 CanInterface() bool : 是否可以调用Interface方法 获取信息 Kind() Kind :持有值的基础类型,零值返回Invalid(0) Type() Type :持有值的Type Addr() Value :指向持有值的指针,不可寻址会panic UnsafeAddr() uintptr :指向持有值的指针,不可寻址会panic Interface() (i interface{}) :以interface{}类型返回持有的值 设置值 Set(x Value) :将持有值修改为x的持有值,不可寻址会panic SetZero() :将持有值设置为类型的零值
基础类型
CanConvert(t Type) bool :持有值是否可以转换为t类型 Convert(t Type) Value :将持有值转换为t类型的值,并返回该值的Value Bool() bool :Kind不是Bool会panic SetBool(x bool) Bytes() []byte :[]byte,持有值[]byte会panic SetBytes(x []byte) String() string :持有值的字符串表示 :Kind不是String返回""格式字符串 SetString(x string) CanInt() bool :有符号整数 OverflowInt(x int64) bool:持有值的类型表示x时是否会溢出 Int() int64 :Kind不是Int、Int8、Int16、Int32、Int64会panic SetInt(x int64) CanUint() bool :无符号整数 OverflowUint(x uint64) bool Uint() uint64 :Kind不是Uint、Uintptr、Uint8、Uint16、Uint32、Uint64会panic SetUint(x uint64) CanFloat() bool :浮点数 OverflowFloat(x float64) bool Float() float64 :Kind不是Float32、Float64会panic SetFloat(x float64) CanComplex() bool :复数 OverflowComplex(x complex128) bool Complex() complex128 :Kind不是Complex64、Complex128会panic SetComplex(x complex128)
结构体相关
字段相关 NumField() int :持有值结构体字段数 Field(i int) Value :持有值结构体的第i个字段 FieldByIndex(index []int) Value :持有值结构体的嵌套索引字段,等价于用索引中的值链式调用本方法 FieldByName(name string) Value :持有值名为name的字段(会查找匿名字段及其子字段),未找到返回Value零值 FieldByNameFunc(match func(string) bool) Value :持有值第一个字段名满足match的字段(会查找匿名字段及其子字段),未找到返回Value零值 方法相关 NumMethod() int :持有值的方法集的方法数目 Method(i int) Value :持有值第i个方法,如果i出界或持有值是接口类型的零值(nil)会panic 返回值调用Call方法时不应包含接收者 返回值持有的函数总是使用v的持有者作为接收者(即第一个参数) MethodByName(name string) Value :持有值的名为name的方法,未找到会返回一个Value零值。 返回值调用Call方法时不应包含接收者 返回值持有的函数总是使用v的持有者作为接收者(即第一个参数)
Map相关
MapIndex(key Value) Value :持有值里key持有值为键对应的值,未找到对应值或者v持有值是nil映射会返回Value零值 MapKeys() []Value :持有值中所有键的Value未排序封装切片,持有值是nil返回空切片(非nil) MapRange() *MapIter :返回Map的迭代器 SetMapIndex(key, val Value):添加或修改持有值的键值对,持有值是nil会panic 如果val是Value零值,则是删除键值对 SetIterKey(iter *MapIter) :将iter当前条目的键分配给持有值,等效于v.Set(iter.Key()),但它避免分配新的值 SetIterValue(iter *MapIter):将iter当前条目的值分配给持有值,等效于 v.Set(iter.Value()),但它避免分配新的值
Chan相关
Recv() (x Value, ok bool) :持有通道接收并返回一个值 方法会阻塞直到获取到值 如果x对应于某个发送到持有通道的值,ok为真 如果因为通道关闭而返回,x为Value零值而ok为假 TryRecv() (x Value, ok bool) :非阻塞版本的Recv() 如果成功接收到一个值,会返回该值和true 如果不能无阻塞的接收到值,返回Value零值和false 如果因为通道关闭而返回,返回值x是持有通道元素类型的零值的Value和false Send(x Value) :阻塞的向持有通道发送x持有的值,x的持有值不能直接赋值给持有通道的元素类型会panic TrySend(x Value) bool:非阻塞发送X,返回是否成功发送 Close() :关闭持有通道
Func相关
Call(in []Value) []Value :使用输入的参数in调用持有函数 如len(in) == 3,v.Call(in)代表调用v(in[0], in[1], in[2]) 如果持有函数是可变参数函数,会自行创建一个代表可变参数的切片,将对应可变参数的值都拷贝到里面 CallSlice(in []Value) []Value :调用持有的可变参数函数,不是可变参数函数会panic
切片相关
SetCap(n int):设置持有值的容量 SetLen(n int):设置持有值的长度 Grow(n int) :增加切片的容量以保证为 n 个元素留出空间 执行该函数后至少可以将 n 个元素附加到切片中而无需其他分配 n为负数、n太大而无法分配内存会panic
类数组相关
Slice(i, j int) Value :持有值的子切片(v[i:j]),Kind不是Array、Slice或String会panic Slice3(i, j, k int) Value :是Slice的3参数版本,返回v[i:j:k] Cap() int :持有值的容量,Kind不是Array、Chan、Slice会panic Len() int :持有值的长度,Kind不是Array、Chan、Slice、Map、String会panic Index(i int) Value :持有值的第i个元素,Kind不是Array、Chan、Slice、String,或i出界会panic
指针相关
Pointer() uintptr :将持有值作为一个指针返回 返回值不是unsafe.Pointer类型,以避免不显式导入unsafe包却得到unsafe.Pointer类型表示的指针 Kind不是Chan、Func、Map、Ptr、Slice或UnsafePointer会panic 如果Kind是Func,返回值是底层代码的指针,但并不足以用于区分不同的函数;只能保证当且仅当v持有函数类型零值nil时,返回值为0。 如果Kind是Slice,返回值是指向切片第一个元素的指针,如果切片为nil,返回值为0;如果持有的切片没有元素但不是nil,返回值不会是0 SetPointer(x unsafe.Pointer)
其他操作
Elem() Value :持有的接口保管的值或持有的指针指向的值,Kind不是Interface或Ptr会panic InterfaceData() [2]uintptr :持有的接口类型值的数据,Kind不是Interface会panic Clear() :清除所有元素,Kind不是Map、Slice会panic Comparable() bool :是否具有可比性,即使返回true仍然可能palic,如接口是可比的,但是其动态类型可能不可比 Equal(u Value) bool :两个值是否深度相等,两个无效值也会返回true Seq() iter.Seq[Value] :返回一个迭代器Seq[Value]用于循环遍历持有值的元素 Kind为Func,则必须是一个没有结果的函数,并且对于某种 T 类型,它采用 func(T) bool 类型的单个参数 kind为Pointer,则指针元素类型必须为Array 否则kind必须是Int、Int8、Int16、Int32、Int64、uint、uint8、uint16、uint32、uint64、uintptr、Array、Chan、map、slice 或 string Seq2() iter.Seq2[Value, Value] :返回一个迭代器Seq2[Value, Value]用于循环遍历持有值的元素 Kind为Func,则必须是一个没有结果的函数,并且对于某种类型 K, V,它采用 func(K, V) bool 类型的单个参数 kind为Pointer,则指针元素类型必须具为Array 否则kind必须是Array、Map、Slice 或 String
type Kind
表示一个类型值
type Kind uint reflect包定义了所有可能会用到的数据类型的Kind常量,常用的有 Bool(1)、Int(2)、Int32(5)、Int64(6)、Float32(13)、Float64(14)、 Array(17)、Chan(18)、Func(19)、Interface(20)、 Map(21)、Pointer(22)、Slice(23)、String(24)、Struct(25) Invalid(0)表示非法的未知类型
使用Kind的String()方法获取其字符串表示形式
type StructField
表示结构体中一个字段的信息
type StructField struct { Name string // 字段名 PkgPath string // 非导出字段的包路径,对导出字段该字段为"" Type Type // 字段类型 Tag StructTag // 字段标签 Offset uintptr // 字段在结构体中的字节偏移量 Index []int // 用于Type.FieldByIndex时的索引切片 Anonymous bool // 是否匿名字段 }
包级函数VisibleFields(t Type) []StructField :返回 t 中的所有可以通过FieldByName调用访问到的字段,t必须是结构类型
StructField的IsExported()方法获取该字段是否为导出字段
type StructTag
表示一个字段的标签
type StructTag string
StructTag的方法
Get(key string) 获取key对应的值,如果不存在key返回"" Lookup(key string) (value string, ok bool) 同Get 增加一个是否存在key的布尔返回值用于区分不存在key时返回的""和值为""
type ChanDir
表示通道类型的方向
type ChanDir int const ( RecvDir ChanDir = 1 << iota // <-chan SendDir // chan<- BothDir = RecvDir | SendDir // chan )
使用ChanDir的String()方法获取其字符串表示形式
type Method
表示一个方法
type Method struct { Name string // 方法名 PkgPath string // 非导出字段的包路径,对导出字段该字段为"" Type Type // 方法类型 Func Value // 方法的值 Index int // 用于Type.Method的索引 }
使用Method的IsExported()方法获取方法是否为导出方法
type MapIter
表示一个Map的迭代器
type MapIter struct {}
通过Value的MapRange()方法获取
MapIter的方法
Next() bool :推进迭代器到下一个位置,当iter耗尽时返回false Key() Value :返回当前位置的键 Value() Value:返回当前位置的值 Reset(v Value) :修改迭代器的迭代数据为新的Map
包级函数
Copy(dst, src Value) int
将src中的值拷贝到dst,直到src被耗尽或dst被装满
要求二者都是slice或array,且元素类型相同
返回复制的元素数量
有一种特殊情况,如果dst元素类型是Uint8,则src可以为String
DeepEqual(a1, a2 any) bool :判断两个值是否深度一致
首先会判断类型是否相同,不同类型的值任何时候都不可能深度一致 基本类型会使用==运算符 array、slice会比较长度及成员,空切片不等于nil切片 map会比较键值对数量和键名及对应值,对键名只用==,值会继续往深层比对 结构体字段(含未导出字段)会进行深入比对 接口具有深度相等的具体值,它们就深度一致 指针值使用==运算符 该函数可以正确处理循环的类型 函数类型只有都为nil时才深度一致
Swapper(v any) func(i, j int)
返回一个函数,用于交换提供的切片中的元素,v持有值不是切片类型会panic