全部教程·
Go语言·
Go语言圣经
[目录]
·
12.1. 为何需要反射
Go语言圣经
0.1. Go语言起源
0.2. Go语言项目
0.3. 本书的组织
0.4. 更多的信息
0.5. 致谢
1. 入门
1.1. Hello, World
1.2. 命令行参数
1.3. 查找重复的行
1.4. GIF动画
1.5. 获取URL
1.6. 并发获取多个URL
1.7. Web服务
1.8. 本章要点
2. 程序结构
2.1. 命名
2.2. 声明
2.3. 变量
2.4. 赋值
2.5. 类型
2.6. 包和文件
2.7. 作用域
3. 基础数据类型
3.1. 整型
3.2. 浮点数
3.3. 复数
3.4. 布尔型
3.5. 字符串
3.6. 常量
4. 复合数据类型
4.1 数组
4.2 Slice
4.3 Map
4.4 结构体
4.5 JSON
4.6 文本和HTML模板
5. 函数
5.1. 函数声明
5.2. 递归
5.3. 多返回值
5.4. 错误
5.5. 函数值
5.6. 匿名函数
5.7. 可变参数
5.8. Deferred函数
5.9. Panic异常
5.10. Recover捕获异常
6. 方法
6.1. 方法声明
6.2. 基于指针对象的方法
6.3. 嵌入结构体扩展类型
6.4. 方法值和表达式
6.5. 示例: Bit数组
6.6. 封装
7. 接口
7.1. 接口是合约
7.2. 接口类型
7.3. 实现接口的条件
7.4. flag.Value接口
7.5. 接口值
7.6. sort.Interface接口
7.7. http.Handler接口
7.8. error接口
7.9. 示例: 表达式求值
7.10. 类型断言
7.11. 识别错误类型
7.12. 查询接口
7.13. 类型分支
7.14. XML解码
7.15. 补充几点
8. Go协程和通道
8.1. Goroutines
8.2. 并发的Clock服务
8.3. 并发的Echo服务
8.4. Channels
8.5. 并发的循环
8.6. 并发的Web爬虫
8.7. 多路复用
8.8. 并发的字典遍历
8.9. 并发的退出
8.10. 聊天服务
9. 并发
9.1. 竞争条件
9.2. sync.Mutex互斥锁
9.3. sync.RWMutex读写锁
9.4. 内存同步
9.5. sync.Once初始化
9.6. 竞争条件检测
9.7. 并发的非阻塞缓存
9.8. Goroutines和线程
10. 包和工具
10.1. 包简介
10.2. 导入路径
10.3. 包声明
10.4. 导入声明
10.5. 包的匿名导入
10.6. 包和命名
10.7. 工具
11. 测试
11.1. go test
11.2. 测试函数
11.3. 测试覆盖率
11.4. 基准测试
11.5. 剖析
11.6. 示例函数
12. 反射
12.1. 为何需要反射
12.2. reflect.Type
12.3. 递归打印
12.4. 编码S表达式
12.5. reflect.Value修改值
12.6. 解码S表达式
12.7. 获取结构体字段
12.8. 显示类型的方法集
12.9. 几点忠告
13. 底层编程
13.1. unsafe.Sizeof
13.2. unsafe.Pointer
13.3. 深度相等判断
13.4. cgo调用C代码
13.5. 几点忠告
Go语言圣经
0.1. Go语言起源
0.2. Go语言项目
0.3. 本书的组织
0.4. 更多的信息
0.5. 致谢
1. 入门
1.1. Hello, World
1.2. 命令行参数
1.3. 查找重复的行
1.4. GIF动画
1.5. 获取URL
1.6. 并发获取多个URL
1.7. Web服务
1.8. 本章要点
2. 程序结构
2.1. 命名
2.2. 声明
2.3. 变量
2.4. 赋值
2.5. 类型
2.6. 包和文件
2.7. 作用域
3. 基础数据类型
3.1. 整型
3.2. 浮点数
3.3. 复数
3.4. 布尔型
3.5. 字符串
3.6. 常量
4. 复合数据类型
4.1 数组
4.2 Slice
4.3 Map
4.4 结构体
4.5 JSON
4.6 文本和HTML模板
5. 函数
5.1. 函数声明
5.2. 递归
5.3. 多返回值
5.4. 错误
5.5. 函数值
5.6. 匿名函数
5.7. 可变参数
5.8. Deferred函数
5.9. Panic异常
5.10. Recover捕获异常
6. 方法
6.1. 方法声明
6.2. 基于指针对象的方法
6.3. 嵌入结构体扩展类型
6.4. 方法值和表达式
6.5. 示例: Bit数组
6.6. 封装
7. 接口
7.1. 接口是合约
7.2. 接口类型
7.3. 实现接口的条件
7.4. flag.Value接口
7.5. 接口值
7.6. sort.Interface接口
7.7. http.Handler接口
7.8. error接口
7.9. 示例: 表达式求值
7.10. 类型断言
7.11. 识别错误类型
7.12. 查询接口
7.13. 类型分支
7.14. XML解码
7.15. 补充几点
8. Go协程和通道
8.1. Goroutines
8.2. 并发的Clock服务
8.3. 并发的Echo服务
8.4. Channels
8.5. 并发的循环
8.6. 并发的Web爬虫
8.7. 多路复用
8.8. 并发的字典遍历
8.9. 并发的退出
8.10. 聊天服务
9. 并发
9.1. 竞争条件
9.2. sync.Mutex互斥锁
9.3. sync.RWMutex读写锁
9.4. 内存同步
9.5. sync.Once初始化
9.6. 竞争条件检测
9.7. 并发的非阻塞缓存
9.8. Goroutines和线程
10. 包和工具
10.1. 包简介
10.2. 导入路径
10.3. 包声明
10.4. 导入声明
10.5. 包的匿名导入
10.6. 包和命名
10.7. 工具
11. 测试
11.1. go test
11.2. 测试函数
11.3. 测试覆盖率
11.4. 基准测试
11.5. 剖析
11.6. 示例函数
12. 反射
12.1. 为何需要反射
12.2. reflect.Type
12.3. 递归打印
12.4. 编码S表达式
12.5. reflect.Value修改值
12.6. 解码S表达式
12.7. 获取结构体字段
12.8. 显示类型的方法集
12.9. 几点忠告
13. 底层编程
13.1. unsafe.Sizeof
13.2. unsafe.Pointer
13.3. 深度相等判断
13.4. cgo调用C代码
13.5. 几点忠告
12.1. 为何需要反射?
有时候我们需要编写一个函数能够处理一类并不满足普通公共接口的类型的值,也可能是因为它们并没有确定的表示方式,或者是在我们设计该函数的时候这些类型可能还不存在。
一个大家熟悉的例子是fmt.Fprintf函数提供的字符串格式化处理逻辑,它可以用来对任意类型的值格式化并打印,甚至支持用户自定义的类型。让我们也来尝试实现一个类似功能的函数。为了简单起见,我们的函数只接收一个参数,然后返回和fmt.Sprint类似的格式化后的字符串。我们实现的函数名也叫Sprint。
我们首先用switch类型分支来测试输入参数是否实现了String方法,如果是的话就调用该方法。然后继续增加类型测试分支,检查这个值的动态类型是否是string、int、bool等基础类型,并在每种情况下执行相应的格式化操作。
func Sprint(x interface{}) string {
type stringer interface {
String() string
}
switch x := x.(type) {
case stringer:
return x.String()
case string:
return x
case int:
return strconv.Itoa(x)
// ...similar cases for int16, uint32, and so on...
case bool:
if x {
return "true"
}
return "false"
default:
// array, chan, func, map, pointer, slice, struct
return "???"
}
}
但是我们如何处理其它类似[]float64、map[string][]string等类型呢?我们当然可以添加更多的测试分支,但是这些组合类型的数目基本是无穷的。还有如何处理类似url.Values这样的具名类型呢?即使类型分支可以识别出底层的基础类型是map[string][]string,但是它并不匹配url.Values类型,因为它们是两种不同的类型,而且switch类型分支也不可能包含每个类似url.Values的类型,这会导致对这些库的依赖。
没有办法来检查未知类型的表示方式,我们被卡住了。这就是我们为何需要反射的原因。
下一章:12.2. reflect.Type和reflect.Value
反射是由 reflect 包提供的。 它定义了两个重要的类型, Type 和 Value. 一个 Type 表示一个Go类型. 它是一个接口, 有许多方法来区分类型以及检查它 ...
AI 中文社