注册 登录

清河洛

Go中的反射

qingheluo2024-08-21清河洛302
反射就是程序在运行时,检查变量存储的数据,进而获得其数据类型对于静态类型为接口的变量,其动态类型可以是实现了接口方法的任何数据Go是强类型语言,由于不知道其动态类型的数据类型,也就无法对其进行操作,此时我们需要使用反射来获取其动态类型的数据类型Go的官方reflect包实现了运行时反射,但是需要注意的是反射的性能较差,过多的使用反射会导致程序性能受影响reflect包中的类型type Type表示一个数据类型,并非所有类型的Type都能使用所有方法,调用该类型不支持的方法会导致运行时的panic type Type interface { // 基本信息 Align() ...

反射就是程序在运行时,检查变量存储的数据,进而获得其数据类型

对于静态类型为接口的变量,其动态类型可以是实现了接口方法的任何数据

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


网址导航