存储一个切片的接口{}上的范围

假设您有一个接受 t interface{}的函数。如果确定 t是一个切片,那么如何在该切片上执行 range

func main() {
data := []string{"one","two","three"}
test(data)
moredata := []int{1,2,3}
test(data)
}


func test(t interface{}) {
switch reflect.TypeOf(t).Kind() {
case reflect.Slice:
// how do I iterate here?
for _,value := range t {
fmt.Println(value)
}
}
}

去游乐场示例: http://play.golang.org/p/DNldAlNShB

111913 次浏览

我使用了 reflect.ValueOf,然后如果它是一个片,你可以调用 Len()Index()的值来获得片和元素在索引处的 len。我不认为你能够使用范围操作做到这一点。

package main


import "fmt"
import "reflect"


func main() {
data := []string{"one","two","three"}
test(data)
moredata := []int{1,2,3}
test(moredata)
}


func test(t interface{}) {
switch reflect.TypeOf(t).Kind() {
case reflect.Slice:
s := reflect.ValueOf(t)


for i := 0; i < s.Len(); i++ {
fmt.Println(s.Index(i))
}
}
}

去游乐场示例: http://play.golang.org/p/gQhCTiwPAq

如果你知道期望的类型,你不需要使用反射。你可以使用 型式开关型式开关型式开关,像这样:

package main


import "fmt"


func main() {
loop([]string{"one", "two", "three"})
loop([]int{1, 2, 3})
}


func loop(t interface{}) {
switch t := t.(type) {
case []string:
for _, value := range t {
fmt.Println(value)
}
case []int:
for _, value := range t {
fmt.Println(value)
}
}
}

查看操场上的代码。

接口{}的行为方式有一个例外,@Jeremy Wall 已经给出了指针。如果传递的数据最初定义为[]接口{}。

package main


import (
"fmt"
)


type interfaceSliceType []interface{}


var interfaceAsSlice interfaceSliceType


func main() {
loop(append(interfaceAsSlice, 1, 2, 3))
loop(append(interfaceAsSlice, "1", "2", "3"))
// or
loop([]interface{}{[]string{"1"}, []string{"2"}, []string{"3"}})
fmt.Println("------------------")




// and of course one such slice can hold any type
loop(interfaceSliceType{"string", 999, map[int]string{3: "three"}})
}


func loop(slice []interface{}) {
for _, elem := range slice {
switch elemTyped := elem.(type) {
case int:
fmt.Println("int:", elemTyped)
case string:
fmt.Println("string:", elemTyped)
case []string:
fmt.Println("[]string:", elemTyped)
case interface{}:
fmt.Println("map:", elemTyped)
}
}
}

产出:

int: 1
int: 2
int: 3
string: 1
string: 2
string: 3
[]string: [1]
[]string: [2]
[]string: [3]
------------------
string: string
int: 999
map: map[3:three]

试试看

在 masebase 提供的答案的基础上,您可以通过如下函数在 interface{}片上泛化迭代:

func forEachValue(ifaceSlice interface{}, f func(i int, val interface{})) {
v := reflect.ValueOf(ifaceSlice)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() != reflect.Slice {
panic(fmt.Errorf("forEachValue: expected slice type, found %q", v.Kind().String()))
}


for i := 0; i < v.Len(); i++ {
val := v.Index(i).Interface()
f(i, val)
}
}

然后,你像这样使用它:

func main() {
data := []string{"one","two","three"}
test(data)
moredata := []int{1,2,3}
test(data)
}


func test(sliceIface interface{}) {
forEachValue(sliceIface, func(i int, value interface{}) {
fmt.Println(value)
}
}

功能上的共同点

import (
"fmt"
"reflect"
)


func In(item interface{}, list interface{}) (ret bool, err error) {
defer func() {
if r := recover(); r != nil {
ret, err = false, fmt.Errorf("%v", r)
}
}()
itemValue, listValue := reflect.ValueOf(item), reflect.ValueOf(list)
for i := 0; i < listValue.Len(); i++ {
if listValue.Index(i).Interface() == itemValue.Interface() {
return true, nil
}
}
return false, nil
}

不是那么优雅,但效果很好

fmt.Println(In(1, []int(2, 1, 3)))