要实现它 , 非常简单 。
func (p Pill) String() string { switch p { case Placebo: return "Placebo" case Aspirin: return "Aspirin" case Ibuprofen: return "Ibuprofen" case Paracetamol: // == Acetaminophen return "Paracetamol" } return fmt.Sprintf("Pill(%d)", p)}
试想 , 如果我们的Pill名单里新增了一批药品名 , 每次增加或修改药品名 , 在相应的签名函数里 , 也都需要进行更改 。这样岂不是很麻烦且很可能遗漏或出错?这时 , 我们可以通过 go generate + stringer的方案解决该问题 。很简单 , 只需在定义Pill的代码中 , 增加一句注释语句即可 。
//go:generate stringer -type=Pill
上面的命令 , 代表运行stringer工具来为Pill类型生成String方法 , 默认输出到pill_string.go文件中 , 执行如下 。
$ go generate$ cat pill_string.go// Code generated by stringer -type Pill pill.go; DO NOT EDIT.package painkillerimport "fmt"const _Pill_name = "PlaceboAspirinIbuprofenParacetamol"var _Pill_index = [...]uint8{0, 7, 14, 23, 34}func (i Pill) String() string { if i < 0 || i+1 >= Pill(len(_Pill_index)) { return fmt.Sprintf("Pill(%d)", i) } return _Pill_name[_Pill_index[i]:_Pill_index[i+1]]}
这样 , 每次我们对Pill类型有修改时 , 我们所需要做的就是运行以下语句即可 。
$ go generate
当然 , 你要是觉得这样麻烦 , 或者担心忘记执行generate语句 。那么 , 可以将go generate语句写入Makefile之中 , 置于go build命令之前 , 实现代码生成与编译的自动化 。
值得一提的是 , 在Go源码文档中 , 大量采用了go generate+stringer的方案实现对枚举常量的String方法 。在小菜刀本机Go 1.14.1的源码下 , 一共有23处使用 , 具体如下 。
文章插图
总结
本文主要介绍generate是什么 , 能做什么 , 如果想深入理解其内在实现逻辑 , 可以去看Go源码中生成代码的详细过程 , 例如sort包下通过genzfunc.go实现zfuncversion.go的生成 。在Go源码宝库中 , 可以找到很多相似的实现逻辑 , 参照如下 。
文章插图
它们利用Go编译器提供的库 , 包括定义抽象语法树的 go/ast、解析抽象语法树的go/parser、解析用于格式化代码的 go/format、用于Go词法标记的go/token等 。解析源文件并按照已有的模板生成新的代码 , 这一过程和Web 服务中利用模板生成 html 文件类似 。
总结:减少代码的重复编写 , 保护头发!!
参考
https://golang.org/cmd/go/
https://blog.golang.org/generate
https://godoc.org/golang.org/x/tools/cmd/stringer
https://docs.google.com/document/d/1V03LUfjSADDooDMhe-_K59EgpTEm3V8uvQRuNMAEnjg/edit#
推荐阅读
- Linux下9种优秀的代码比对工具推荐
- 为什么用抓包工具看HTTPS包是明文的?
- 最好用的PDF文档处理工具,不接受反驳
- 茶水之梅兰竹菊,武夷岩茶培火程度对口感的影响
- 解除USB限制原来如此简单
- 茶山谱之景迈,景迈古茶山茶质特色
- 阻塞队列实现生产者消费者以及同步工具类
- 茶叶酦酵之学理,文山包种的鉴别
- 茶叶的鉴赏之道,来自东方的调和之道
- 淘宝常用的付费推广方式 淘宝付费推广工具都有哪些