猿问

切片和接口的类型转换

如果我传递了一个函数,我可以确定(通过其他方式)是一个切片,但不是它是什么切片,我该如何迭代它(最好不使用反射)?interface{}


这是一个MCVE(从我的实际代码中非常简化)。 是一个循环访问切片的函数,其类型在 main() 的调用堆栈中“丢失”。它试图说“啊哈,一个切片,我将循环访问它的一个接口切片{},并在每个值上调用传入的onEach函数”。这失败了,因为类型“转换”失败了,因此:forEach


panic: interface conversion: interface {} is []string, not []interface {}

我非常清楚为什么类型“转换”失败,即它不是真正的类型转换,而是一个断言。但是,给定(如果只有我可以迭代)我可以断言每个切片成员,这在原则上应该是可行的。


让我们假设我实际上想要一个可以做到这一点的迭代器(而不是一个,等等)。有没有一个好方法可以做到这一点?最好没有反射(虽然我想这没关系),但肯定没有涉及每种类型案例的反射(这是首先具有函数的意义)?forEachforEachStringforEachIntforEach


我知道(尚未实现的)泛型提案将在这方面非常有效,但我想用现有的技术来做到这一点!


package main


import (

    "fmt"

)


type onEach func(x interface{})


func printString(x interface{}) {

    xx := x.(string)

    fmt.Printf("x is a string '%s'\n", xx)

}


func printInt(x interface{}) {

    xx := x.(int)

    fmt.Printf("x is an int '%d'\n", xx)

}


func forEach(y interface{}, onEach onEach) {

    // code to ensure y is a slice omitted

    a := y.([]interface{}) // <-------- THIS LINE PANICS

    for _, x := range a {

        onEach(x)

    }

}


func main() {

    s := []string{"foo", "bar"}

    i := []int{1, 2, 3}

    forEach(s, printString)

    forEach(i, printInt)

}


长风秋雁
浏览 149回答 2
2回答

烙印99

所以这里有一个使用反射的答案,我想这不会太难看。package mainimport (&nbsp; &nbsp; "fmt"&nbsp; &nbsp; "reflect")type onEach func(x interface{})func printString(x interface{}) {&nbsp; &nbsp; xx := x.(string)&nbsp; &nbsp; fmt.Printf("x is a string '%s'\n", xx)}func printInt(x interface{}) {&nbsp; &nbsp; xx := x.(int)&nbsp; &nbsp; fmt.Printf("x is an int '%d'\n", xx)}func forEach(y interface{}, onEach onEach) {&nbsp; &nbsp; // code to ensure y is a slice omitted&nbsp; &nbsp; v := reflect.ValueOf(y)&nbsp; &nbsp; for i := 0; i < v.Len(); i++ {&nbsp; &nbsp; &nbsp; &nbsp; onEach(v.Index(i).Interface())&nbsp; &nbsp; }}func main() {&nbsp; &nbsp; s := []string{"foo", "bar"}&nbsp; &nbsp; i := []int{1, 2, 3}&nbsp; &nbsp; forEach(s, printString)&nbsp; &nbsp; forEach(i, printInt)}

波斯汪

使用反射包在任意类型的切片上编写迭代函数:// forEach calls f for each element of slice s.// The function f must have a single argument with// the same type as the slice's element type.func forEach(s interface{}, f interface{}) {&nbsp; &nbsp; sv := reflect.ValueOf(s)&nbsp; &nbsp; fv := reflect.ValueOf(f)&nbsp; &nbsp; for i := 0; i < sv.Len(); i++ {&nbsp; &nbsp; &nbsp; &nbsp; fv.Call([]reflect.Value{sv.Index(i)})&nbsp; &nbsp; }}像这样使用它:func printString(s string) {&nbsp; &nbsp; fmt.Printf("x is a string %q\n", s)}s := []string{"foo", "bar"}forEach(s, printString)此答案与问题中的代码和另一个答案不同,因为该函数不需要使用类型评估。f
随时随地看视频慕课网APP

相关分类

Go
我要回答