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


96

给定您具有接受功能的场景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


为什么不让函数使用[] interface {}呢?您是否要处理多种复杂类型?
Jeremy Wall

2
是的,它用于模板系统,所以interface {}可以是映射,结构,切片或数组。在实际的代码中,还有更多的case语句,但是我从帖子中删除了它们,以使问题更简洁。
Nucleon

1
Jeremy,[]字符串不是[] interface {}的子类型,因此您不能使用[] string或[] int等调用func([] interface {})函数。 Go中有一个功能,其类型的意思是“某物的切片”,您可以在其中迭代作为interface {}的元素,但是不幸的是,您需要对此进行反思。
Bjarke Ebert

@JeremyWall您不能使用[] interface {}来充当切片。您可以在这里参考。
firelyu

Answers:


137

好吧,我曾经用过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


23
效果很好,谢谢。唯一要添加的是s.Index(i)返回a,reflect.Value因此我需要s.Index(i).Interface()参考实际值。
Nucleon

1
如果您有指向切片的指针,该interface{}怎么办?例如 moredata := &[]int{1,2,3}
Ryan Walls

1
回答我的问题:如果您有一个指向切片而不是切片的指针,则需要使用Elem()来获取基础值。例如 reflect.TypeOf(reflect.ValueOf(t).Elem().Interface()).Kind()
Ryan Walls'6

如果我想从没有任何结构的接口切片中获取接口字段的值怎么办?我已经尝试过此解决方案,但仅限于从片而不是字段的实际值获取接口引用。
Amandeep kaur

非常感谢,这个技巧为我节省了很多时间(和头痛!)
Nicolas Garnier

26

如果您知道需要哪种类型,则无需使用反射。您可以使用类型开关,如下所示:

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

在操场上检查代码


但是,这将无法在数组上工作,只能在切片上工作
user1028741

@ user1028741不,代码适用于包括数组在内的每种类型+您始终可以从数组中获取一个切片array[:]
伊南克·古姆斯

但是数组的类型将取决于它的实际容量。[3] int与[2] int或[] int的类型不同。因此,如果“ t”的类型实际上是数组,则相关情况将不适用。
user1028741

我的意思是您的示例不适用于数组。我在这里更改过:play.golang.org/p/DAsg_0aXz-r
user1028741

如果您一开始不知道参数的类型,就很难使其工作。为了使用技巧(array [:]),您必须使用反射来确定它是一个数组,然后强制转换为array-然后生成它的切片。这就是为什么我说它在切片而不是数组上起作用。显然,只要有足够的努力,您就可以从阵列中产生一个切片...
user1028741

4

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]

试试看

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.