GO编程模式学习笔记系列为学习陈皓的GO编程模式系列文章记录与心得。
原文链接:GO 编程模式:GO GENERATION
Go语言代码生成主要还是用来解决编程泛型的问题。Go语言的类型检查有两种技术,一种是 Type Assert,一种是Reflection。
反射实现一个支持任何类型的slice(但是必须是同一类型,这也符合范型定义)
type Container struct {
s reflect.Value
}
func NewContainer(t reflect.Type, size int) *Container {
if size <=0 { size=64 }
return &Container{
s: reflect.MakeSlice(reflect.SliceOf(t), 0, size),
}
}
func (c *Container) Put(val interface{}) error {
if reflect.ValueOf(val).Type() != c.s.Type().Elem() {
return fmt.Errorf(“Put: cannot put a %T into a slice of %s",
val, c.s.Type().Elem()))
}
c.s = reflect.Append(c.s, reflect.ValueOf(val))
return nil
}
func (c *Container) Get(refval interface{}) error {
if reflect.ValueOf(refval).Kind() != reflect.Ptr ||
reflect.ValueOf(refval).Elem().Type() != c.s.Type().Elem() {
return fmt.Errorf("Get: needs *%s but got %T", c.s.Type().Elem(), refval)
}
reflect.ValueOf(refval).Elem().Set( c.s.Index(0) )
c.s = c.s.Slice(1, c.s.Len())
return nil
}
代码生成
上述代码可读性稍微有点差,性能较差。如何改进呢?
这样的问题在C++中是通过模板来解决,但是模版就是生成对应代码。只不过是C++的编译器帮你做了这件事,Go语言编译器没有做。在Go实现范型之前,可以自己动手。
要玩 Go的代码生成,你需要三件事:
- 一个函数模板,其中设置好相应的占位符。
- 一个脚本,用于按规则来替换文本并生成新的代码。
- 一行注释代码
go generate 命令本身支持代码生成在,写好模板和替换脚本,在使用的时候加一行注释即可
//go:generate ./gen.sh ./template/filter.tmp.go gen Employee filter
func filterEmployeeExample() {
...
更进一步,有第三方工具可以生成代码:
https://github.com/cheekybits/genny/blob/master/parse/test/slice/ensure_slice_test.go