-
- GO语言学习笔记-函数篇 Study for Go ! Chapter three - Function
- GO语言学习笔记-数据篇 Study for Go ! Chapter four - Data
- GO语言学习笔记-方法篇 Study for Go ! Chapter five - Method
- GO语言学习笔记-接口篇 Study for Go ! Chapter six - Interface
- GO语言学习笔记-并发篇 Study for Go ! Chapter seven - Concurrency
- GO语言学习笔记-包结构篇 Study for Go ! Chapter eight - Package Structure
- GO语言学习笔记-反射篇 Study for Go ! Chapter nine - Reflect
- GO语言学习笔记-测试篇 Study for Go ! Chapter ten- Test
- GO语言学习笔记-工具链篇 Study for Go ! Chapter eleven - Tool Chain
1. TYPE
-
反射让我们能在运行初期探知对象的类型信息和内存结构,这从一定程度上弥补了静态语言在动态行为上的不足。同时,反射还是实现元编程的重要手段
-
-
在面对类型时,需要区分 TYPE 和 KIND ,前者表示真是类型(静态类型),后者表示其基础机构(底层类型)类别
-
除了通过实际对象获取类型外,也可直接构造一些基础符合类型
-
传入对象应区分基类型和指针类型,因为它们并不属于同一类型
-
方法 Elem 返回指针、数组、切片、字典 (值)或通道的基类型
-
只有在获取结构体指针的基类型之后,才能遍历它的字段
-
对于匿名字段,可用多级索引 (按定义顺序)直接访问
-
FieldByName 不支持多级名称,如有同名遮蔽,须通过匿名字段二次获取
-
同样地,输出方法集时,一样区分基类型和指针类型
-
有一点和想象的不同,反射能探知当前包或外包的非导出结构成员 (相对 reflect 而言,当前包和外包都是 “ 外包 ”)
-
可用反射提取 struct tag,还能自动分解。其常用于 ORM 映射,或数据格式验证
-
辅助判断方法 Implements、Convertible、AssignableTo 都是运行期进行动态调用和赋值所必须的。
2. Value
-
和 TYPE 获取类型信息不同,Value 专注于对象实例数据读写
-
接口变量会复制对象,且是 Unaddressable 的,所以想要修改目标对象,就必须要使用指针
-
但是就算传入指针,一样需要通过 Elem 获取目标对象。因为被接口存储的指针本身是不能寻址和进行设置操作的
Attention:
不能对非导出字段直接进行设置操作,无论是当前包还是外包
-
Value.Pointer 和 Value.Int 等方法类似,将 Value.data 存储的数据转换为指针,目标必须是指针类型。而 UnsafeAddr 返回任何 CanAddr Value.data 地址 (相当于 & 取地址操作),比如 Elem 后的 Value,以及字段成员地址
-
以结构体里的指针类型字段为例,Pointer 返回该字段所保存的地址,而 UnsafeAddr 返回该字段自身的地址 (结构对象地址 + 偏移量)
-
可通过 Interface 方法进行类型推断和转换
-
也可以直接使用 Value.Int、Bool 等方法进行类型转换,但失败时会引发 Panic,且不支持 ok-idom
-
接口有两种 nil 状态,这是一个潜在的麻烦。解决的办法是用 IsNil 判断值是否为 nil
-
也可用 unsafe 转换后直接判断 iface.data 是否为零值
-
让人无奈的是,Value 里的某些方法 并未实现 ok-idom 或返回 error,所以得自行判断返回的是否为 Zero Value
3. Method
-
动态调用方法,谈不上有多麻烦,只需要按 In 列表准备好 所需参数即可
-
对于变参来说,用 CallSlice 要更方便一些
-
无法调用非导出方法,甚至无法获取有效地址
4. 构建
-
反射库提供了内置函数 make 和 new 的对应操作,其中最有意思的就是 MakeFunc。可以用它实现通用模板,适应不同类型数据
-
golang 暂不支持泛型,所以会麻烦一点
5.性能
-
反射在带来“ 方便 ” 的同时,也造成了很大的困扰,很多人对反射避之不及,因为它会造成很大的性能损失。
-
如果对性能要求较高,那么须谨慎使用反射