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

时间:2012-12-24 21:28:44

标签: go reflection slice go-reflect

考虑到你有一个接受t interface{}的功能的情况。如果确定t是一个切片,我如何range超过该切片?在编译时,我不会知道传入的类型,例如[]string[]int[]MyType

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

5 个答案:

答案 0 :(得分:101)

我使用了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

答案 1 :(得分:11)

如果您知道期望哪种类型,则无需使用反射。您可以使用type switch,如下所示:

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)
        }
    }
}

Check out the code on the playground

答案 2 :(得分:1)

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

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]

try it out

答案 3 :(得分:1)

扩展 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)
    }
}

答案 4 :(得分:0)

一个共同的功能

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)))