「这是我参与2022首次更文挑战的第21天,活动详情查看:2022首次更文挑战」
package reflect
建议对着源码看
包说明
文档中指出reflect包实现了运行时反射。 什么是反射,反射允许程序操纵任意类型的对象。 通常用法是:对于一个接口变量,使用TypeOf来提取动态类型,她会返回一个Type。
其他编程语言上的反射: 反射是一种能力,就是程序可以在运行时检查其右拥有的结构,特别是类型。 不同语言对反射机制的实现是不同的,Go使用的reflect包。 是元编程的一种形式。
Rob的反射三定律:
- 接口对象可以转成反射对象
- 反射对象可以转成接口对象,interface{}
- 反射对象如果要修改,要符合传址。结构体中只有暴露字段才具有可修改性
包结构分析
除了测试文件,主要包含5个源文件: type.go value.go表示反射对象中的类型和值; swapper.go 提供了一个封装函数来交换切片中的两个元素; makefunc.go 封装了一些跟函数相关的东西,后面详细看; deepequal.go 底层比较两个变量是否一致,这个用的比较多。
总体说来,type.go是基础,value.go算是补全了reflect的绝大部分功能, 其他的是基于这两个文件来扩展的,所以先分析这两个。
Type
这里的Type是指Go的类型,type Type interface {...}, Type是一个接口类型,她里面包含了很多方法集,并不是所有方法都适用于Go的任意类型, 具体还是要看文档的说明,一般是先调用Kind方法,根据类型再调指定的方法, 好处是不会panic。
Type是支持==比较的,所以可以作为map的key。
下面列一下Type接口的方法集:
- 适用于任意Go类型的方法集
- 内存对齐是几字节
- 结构体字段对齐是几字节
- Method(int),获取第几个方法
- 参数不能超出范围
- 对于非接口类型,返回一个方法的Type和函数字段(接收者作为第一个参数)
- 对于接口类型,返回方法的Type(就是方法的签名),函数字段是nil
- 只能访问对外暴露的方法,按字典顺序排序
- 按方法名获取指定方法
- 计算暴露的方法个数
- 获取包定义的类型名,未定义类型返回空字符串
- 获取包路径,就是包的导入路径,如果是预定义类型或未定义类型,返回空字符串
- 获取内存存储的大小,和unsafe.Sizeof得到的一致
- 支持打印
- Kind获取类型的种类,后面会提到具体哪些种类,底层类型,不区分别名系统
- 是否实现了某个接口
- 是否可赋值给某个类型的变量
- 是否可转换成某个类型
- 是否支持比较,这几个都是针对具体类型的,Type接口类型是支持比较的
- 依赖具体类型,而适用的方法集
- Bits 获取类型的位数(bit,不是字节数),适用于整形/浮点/复数
- ChanDir 获取信道方向,只适用于信道
- IsVariadic 是否支持可变参,只适用于函数
- Elem 获取元素的具体类型,只适用于数组/信道/map/指针/切片
- Field 获取结构体的第几个字段,只适用于结构体,范围不能越界
- FieldByIndex 获取嵌套结构体的字段
- FieldByName 获取结构体的指定名称的字段
- FieldByNameFunc 广度优先寻找字段,只适用于结构体,特别适合有嵌入字段的
- In 获取第几个入参的类型,只适用于函数,范围不能越界
- Key 返回map的key类型
- Len 返回数组的长度
- NumField 返回结构体的字段个数
- NumIn 返回函数入参个数
- NumOut 返回函数返回值个数
- Out 返回函数第几个返回值的类型
之后定义了Kind,就是一个枚举,列举了所有内置类型
还有一个rtype结构体,也做了很多事,可以在后面遇到了再分析。
Value
和Type类似,都有好多方法,不过Value是结构体, 其中Interface是将反射对象转成接口对象,Elem用于修改反射对象的值等
其他封装
DeepEqual比较两个数据的底层结构,都是比较常用的。