Go 高手(25):代码生成——go generate、stringer、自定义 generator
更新时间:2026-09-01。本文是
languages/go/intermediate/高手层第 25 篇,接 多平台编译。Go 社区习惯用代码生成解决重复性工作。go generate运行代码生成器,常用的stringer工具自动生成枚举值的String()方法。你也可以写自己的代码生成器。
本文要回答的问题
go generate是什么?和go build有什么区别?stringer工具怎么用?怎么自动生成枚举的String()方法?- 自定义代码生成器怎么写?输入输出是什么?
- 什么时候需要代码生成?什么时候不需要?
一、go generate 基本用法
go generate 就是运行注释里写的命令,命令生成 Go 代码,然后你再 go build。
go
//go:generate go run generator.go -output out.go input.go然后:
bash
go generate ./...
# 运行所有 //go:generate 命令
# 生成 out.go,然后 go build和 go build 的区别:
go generate在 编译前运行,生成代码- 生成的代码会被
go build编译进去 - 生成的代码需要提交到版本控制,也可以在 CI 里自动生成
二、stringer:自动生成枚举 String()
go
//go:generate go run golang.org/x/tools/cmd/stringer -type=Pill
type Pill int
const (
Aspirin Pill = iota
Ibuprofen
Paracetamol
)运行 go generate:
bash
go generate
# 生成 pill_string.go生成的 pill_stringer.go 自动提供:
go
func (p Pill) String() string {
switch p {
case Aspirin:
return "Aspirin"
case Ibuprofen:
return "Ibuprofen"
case Paracetamol:
return "Paracetamol"
default:
return "Pill(%d)"
}
}不用手动写 String(),自动生成。
安装 stringer:
bash
go install golang.org/x/tools/cmd/stringer@latest三、常见的代码生成场景
- 枚举值:用
stringer生成String() - JSON 序列化:自动生成
MarshalJSON - Mock 接口:用
mockgen生成 mock - ORM 代码:根据表结构自动生成结构体
- CLI flags:根据配置自动生成 flag 解析
四、自定义代码生成器
写一个简单的代码生成器,生成 setter 方法:
go
// generator.go
package main
import (
"os"
"text/template"
)
type Data struct {
StructName string
Fields []Field
}
type Field struct {
Name string
Type string
}
var setterTmpl = `
{{- range .Fields}}
func (s *{{$.StructName}}) Set{{.Name | Title}}(v {{.Type}}) {
s.{{.Name}} = v
}
{{end}}
`
func main() {
// 读模板,渲染,写文件
tmpl := template.Must(template.New("setter").Funcs(template.FuncMap{
"Title": strings.Title,
}).Parse(setterTmpl))
data := Data{
StructName: "User",
Fields: []Field{
{"Name", "string"},
{"Age", "int"},
},
}
f, _ := os.Create("setters_generated.go")
defer f.Close()
tmpl.Execute(f, data)
}在你的包中:
go
//go:generate go run generator.go
type User struct {
Name string
Age int
}运行 go generate → 生成 setters_generated.go。
五、代码生成 vs 反射
| 对比 | 代码生成 | 反射 |
|---|---|---|
| 运行时性能 | 更快,编译期生成代码 | 慢,动态查找 |
| 代码体积 | 多了一些代码 | 不生成额外代码 |
| 调试 | 能看到生成的代码,容易调试 | 动态查找,难调试 |
| 维护 | 需要重新生成代码 | 不用维护,直接用 |
什么时候用代码生成?
- 性能敏感,需要编译期处理 → 代码生成
- 通用性强,不需要性能 → 反射
六、常见坑对照
| 坑 | 现象 | 对策 |
|---|---|---|
//go:generate 和注释之间有空格 | 命令不执行 | //go:generate,空格在 // 和 go 之间 |
| 生成的代码不提交到版本控制 | CI 编译找不到 | 生成的代码提交,或者 CI 跑 go generate |
| 每次编译都重新生成 | 重复生成,慢 | go generate 只在生成改变后跑一次 |
| 生成器自己依赖外部工具 | 环境不一致生成结果不同 | 用 go install 安装依赖,保证一致版本 |
相关与延伸
进阶层现在一共 26 篇,覆盖了 Go 从基础底层到生产实践的进阶知识。接下来看专家层,pprof 性能剖析;代码生成本质上是自动化,避免重复性工作,和 C 语言的宏对比,见 C 预处理。
一句话总结
Go 代码生成:go generate 在编译前运行命令,生成 Go 代码;stringer 工具自动生成枚举的 String() 方法;自定义代码生成器可以根据输入模板渲染输出;代码生成比反射快,适合性能敏感场景;生成的代码能看到,比反射好调试;//go:generate 语法,运行前保证工具已经安装。