Go - 以任意类型的slices作为输入参数(译)
youngsterxyf · · 5841 次点击 · · 开始浏览原文:Go – taking slices of any type as input parameters
译者:youngsterxyf
最近参与的一个业余项目,go-linq,让我了解到Go语言的类型系统并不是为任何类面向 对象编程而设计的。没有泛型,没有类型继承,也没有提供任何对这些特性有用的东西。
但是,提供了一个名为interface{}的类型,你可以向其赋予几乎任意类型的值,不会抛出编译错误,就像.NET的Object或Java的Object:
var o interface{} o := 3.14 o := Student{Name: "Ahmet"}
我们假设你需要一个可以接收任意类型slices的函数,如果考虑如下这样实现:
func Method(in []interface{}){...} ... slice := []int{1, 2, 3} Method(slice) // 抛出错误
这样的代码会抛出编译错误,因为[]int不是[]interface{}。那么该如何解决这个问题呢?你可以要求Method的使用者先把slices
转换为[]interface{}类型。也就是说他们必须借助于如下类似函数将他们的[]AnyType类型参数转换为[]interface{}类型:
func conv(in []AnyType) (out []interface{}) { out = make([]interface{}, len(in)) for i, v := range in { out[i] = v } return }
但这种实现的扩展性并不好。如果Method的使用者(可以是一个常用函数如Map、Filter等)想向Method传递N种不同类型的参数,
那么他们就必须编写N个conv函数。
对此,我们该怎么办呢?使用reflection(反射)呀!实现一个函数以interface{}(可以赋任意类型的值)为输入参数类型,在函数内部
将这个输入参数转换为一个slice,然后用于我们Method函数。如下所示:
func takeSliceArg(arg interface{}) (out []interface{}, ok bool) { slice, success := takeArg(arg, reflect.Slice) if !success { ok = false return } c := slice.Len() out = make([]interface{}, c) for i := 0; i < c; i++ { out[i] = slice.Index(i).Interface() } return out, true } func takeArg(arg interface{}, kind reflect.Kind) (val reflect.Value, ok bool) { val = reflect.ValueOf(arg) if val.Kind() == kind { ok = true } return }
函数takeArg()尝试将传入的参数值转换为指定的reflect.Kind类型,然后函数takeSliceArg()
尝试将传递给它的值(经takeArg()转换后)转换为一个interface{}的slice。虽然,这样会因为反射而影响到一点性能,但影响并不大。
就是这样了。这种方案启发于Tobia Confronto的fn项目,并应用到go-linq中。
有疑问加站长微信联系(非本文作者)
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
关注微信- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码` - 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传
收入到我管理的专栏 新建专栏
原文:Go – taking slices of any type as input parameters
译者:youngsterxyf
最近参与的一个业余项目,go-linq,让我了解到Go语言的类型系统并不是为任何类面向 对象编程而设计的。没有泛型,没有类型继承,也没有提供任何对这些特性有用的东西。
但是,提供了一个名为interface{}的类型,你可以向其赋予几乎任意类型的值,不会抛出编译错误,就像.NET的Object或Java的Object:
var o interface{} o := 3.14 o := Student{Name: "Ahmet"}
我们假设你需要一个可以接收任意类型slices的函数,如果考虑如下这样实现:
func Method(in []interface{}){...} ... slice := []int{1, 2, 3} Method(slice) // 抛出错误
这样的代码会抛出编译错误,因为[]int不是[]interface{}。那么该如何解决这个问题呢?你可以要求Method的使用者先把slices
转换为[]interface{}类型。也就是说他们必须借助于如下类似函数将他们的[]AnyType类型参数转换为[]interface{}类型:
func conv(in []AnyType) (out []interface{}) { out = make([]interface{}, len(in)) for i, v := range in { out[i] = v } return }
但这种实现的扩展性并不好。如果Method的使用者(可以是一个常用函数如Map、Filter等)想向Method传递N种不同类型的参数,
那么他们就必须编写N个conv函数。
对此,我们该怎么办呢?使用reflection(反射)呀!实现一个函数以interface{}(可以赋任意类型的值)为输入参数类型,在函数内部
将这个输入参数转换为一个slice,然后用于我们Method函数。如下所示:
func takeSliceArg(arg interface{}) (out []interface{}, ok bool) { slice, success := takeArg(arg, reflect.Slice) if !success { ok = false return } c := slice.Len() out = make([]interface{}, c) for i := 0; i < c; i++ { out[i] = slice.Index(i).Interface() } return out, true } func takeArg(arg interface{}, kind reflect.Kind) (val reflect.Value, ok bool) { val = reflect.ValueOf(arg) if val.Kind() == kind { ok = true } return }
函数takeArg()尝试将传入的参数值转换为指定的reflect.Kind类型,然后函数takeSliceArg()
尝试将传递给它的值(经takeArg()转换后)转换为一个interface{}的slice。虽然,这样会因为反射而影响到一点性能,但影响并不大。
就是这样了。这种方案启发于Tobia Confronto的fn项目,并应用到go-linq中。