如果我有一个函数传递了一个 interface{}
,我可以确定(通过其他方式)是一个切片,但不是它是什么切片,我如何遍历它(最好不使用反射)?
这是一个 MCVE(从我的实际代码中非常简化)。 forEach
是一个遍历切片的函数,切片的类型在 main() 的调用堆栈中“丢失”。它试图说“啊哈,一个切片,我将遍历它一个 interface{}
的切片,并对每个值调用传入的 onEach
函数”。这将失败,因为类型“转换”因此失败:
panic: interface conversion: interface {} is []string, not []interface {}
我很清楚为什么类型“转换”失败,即它不是真正的类型转换,而是一个断言。然而,鉴于(如果我可以迭代)我可以对每个切片成员进行断言,这原则上应该是可行的。
假设我实际上想要一个像 forEach
这样的迭代器可以做到这一点(而不是 forEachString
、forEachInt
等)。有没有好的方法可以做到这一点?最好没有反射(虽然我想这没问题),但肯定没有涉及每种类型的案例的反射(这是首先拥有 forEach
函数的重点)?
我知道(尚未实施的)泛型提案对此非常有效,但我想用现有技术来做到这一点!
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)
}
答案 0 :(得分:1)
所以这是一个使用反射的答案,我想这不会太难看。
package main
import (
"fmt"
"reflect"
)
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
v := reflect.ValueOf(y)
for i := 0; i < v.Len(); i++ {
onEach(v.Index(i).Interface())
}
}
func main() {
s := []string{"foo", "bar"}
i := []int{1, 2, 3}
forEach(s, printString)
forEach(i, printInt)
}
答案 1 :(得分:1)
使用反射包在任意类型的切片上编写迭代函数:
// 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{}) {
sv := reflect.ValueOf(s)
fv := reflect.ValueOf(f)
for i := 0; i < sv.Len(); i++ {
fv.Call([]reflect.Value{sv.Index(i)})
}
}
像这样使用它:
func printString(s string) {
fmt.Printf("x is a string %q\n", s)
}
s := []string{"foo", "bar"}
forEach(s, printString)
这个答案与问题和另一个答案中的代码不同,因为函数 f
不需要使用类型断言。