存储切片的接口上的范围
Posted
技术标签:
【中文标题】存储切片的接口上的范围【英文标题】:range over interface which stores a slice存储切片的接口上的范围 【发布时间】:2012-12-11 03:22:36 【问题描述】:假设您有一个接受t interface
的函数。如果确定t
是一个切片,我如何range
覆盖那个切片?
func main()
data := []string"one","two","three"
test(data)
moredata := []int1,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 来代替?您是否尝试处理多种复杂类型? 是的,它用于模板系统,因此 interface 可以是映射、结构、切片或数组。实际代码中还有很多case语句,但为了让问题更简洁,我把它们从帖子中删除了。 Jeremy,[]string 不是 []interface 的子类型,因此不能使用 []string 或 []int 调用 func([]interface) 函数等。如果在 Go 中有一个特性,可以有一个表示“某物切片”的类型,那么您可以在其中迭代元素作为 interface,但不幸的是,您需要为此进行反射。 @JeremyWall 您不能使用 []interface 作为切片。可以参考here。 【参考方案1】:好吧,我使用了reflect.ValueOf
,然后如果它是一个切片,您可以调用Len()
和Index()
的值来获取切片和索引处元素的len
。我认为您无法使用范围操作来执行此操作。
package main
import "fmt"
import "reflect"
func main()
data := []string"one","two","three"
test(data)
moredata := []int1,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
【讨论】:
效果很好,谢谢。唯一要添加的是s.Index(i)
返回一个reflect.Value
,所以在我的情况下,我需要s.Index(i).Interface()
来引用实际值。
如果你有一个指向interface
中切片的指针,你会怎么做?例如moredata := &[]int1,2,3
回答我的问题:如果您有指向切片而不是切片的指针,则需要使用 Elem()
来获取基础值。例如reflect.TypeOf(reflect.ValueOf(t).Elem().Interface()).Kind()
如果我想从没有任何结构的接口切片中获取接口字段的值怎么办。我已经尝试过这个解决方案,但仅限于从切片而不是字段的实际值获取接口引用。
非常感谢,这个技巧为我节省了很多时间(也让我头疼!)【参考方案2】:
如果您知道预期的类型,则无需使用反射。你可以使用type switch,像这样:
package main
import "fmt"
func main()
loop([]string"one", "two", "three")
loop([]int1, 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.
【讨论】:
这不会在数组上工作,只能在切片上工作 @user1028741 不,该代码适用于包括数组在内的所有类型 + 您始终可以从数组array[:]
中获取切片。
但是数组的类型以它的实际容量为准。 [3]int 与 [2]int 或 []int 的类型不同。因此,如果 't' 的类型实际上是一个数组,则相关情况将不适用。
我的意思是你的例子不适用于数组。我在这里改了:play.golang.org/p/DAsg_0aXz-r
如果你一开始不知道参数的类型,你就不能轻易地让它工作。为了使用您的技巧(数组 [:]),您必须使用反射来确定它是一个数组,然后转换为数组 - 然后生成它的一部分。这就是为什么我说它适用于切片,而不是数组。显然,只要付出足够的努力,您就可以从数组中产生一个切片......【参考方案3】:
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]string3: "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
【讨论】:
【参考方案4】:扩展 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 := []int1,2,3
test(data)
func test(sliceIface interface)
forEachValue(sliceIface, func(i int, value interface)
fmt.Println(value)
【讨论】:
【参考方案5】:函数中的一个共同点
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)))
【讨论】:
以上是关于存储切片的接口上的范围的主要内容,如果未能解决你的问题,请参考以下文章