【问题标题】:Iterate over an interface迭代一个接口
【发布时间】:2014-11-07 10:11:59
【问题描述】:

我想创建一个函数,它接受一个映射或一个数组,并对其进行迭代,并在每个项目上调用一个函数,该函数知道如何处理它遇到的任何类型。

这是我第一次失败的尝试。目前,当我在实际用例中运行它时,它总是说“哦!”。

func DoTheThingToAllTheThings(data_interface interface{}) int {
    var numThings int

    switch data := data_interface.(type) {
    case map[interface{}]interface{}:
        numThings = len(data)
        for index, item := range data {
            DoTheThing(index, item)
    }
    case []interface{}:
        numThings = len(data)
        for index, item := range data {
            DoTheThing(index, item)
        }
    default:
        fmt.Println("uh oh!")
    }

    return numThings
}

数组或映射可能包含许多不同的东西,因此不能尝试匹配每个可能的输入。

另外说明,有没有办法在 Go 中迭代数组或映射而不知道它到底是什么?

【问题讨论】:

  • 下面的基于reflect 的解决方案将起作用,但惯用的方法是尽可能在调用点写出循环。

标签: types go


【解决方案1】:

为了让您的示例正常工作,您需要构建一个 interface{} 数组(或地图),以便检测到正确的类型:

// This won't work, as the .(type) would be []S
someS := []S{S{}}
DoTheThingToAllTheThings(someS)

// This will: it will go in case []interface{}:
someSI := make([]interface{}, len(someS))
for i, s := range someS {
    someSI[i] = s
}
DoTheThingToAllTheThings(someSI)

查看full example here

但这意味着您仍然可以在 DoTheThing 函数中使用 interface{}
正如我在“What would generics in Go be?”中提到的,这里没有真正的泛型。

【讨论】:

    【解决方案2】:

    函数fmt.Printf("%v\n", data_interface) 完全符合您的要求。它将打印传递给它的整个地图或数组。

    你可以在这里找到实现:http://golang.org/src/pkg/fmt/print.go?h=printArg#L708

    printArg 末尾附近的行是关键:

    return p.printReflectValue(reflect.ValueOf(arg), verb, plus, goSyntax, depth
    

    它使用“反射”包:http://golang.org/pkg/reflect/ 来查询参数的类型。在p.printReflectValue 内部:http://golang.org/src/pkg/fmt/print.go?h=printArg#L862 您将看到处理地图和结构的两种情况。然后它通过printValue 使用递归来管理内容。

    这里有一段代码,它采用结构的反射,然后将其转换回另一个正确类型的变量。您不能使用 this 从一种任意类型更改为另一种,在不使用反射的情况下将类型从一种类型转换为另一种类型必须是合法的。

    package main
    
    import (
        "fmt"
        "reflect"
    )
    
    type Player string
    
    type Board struct {
        Tboard  [9]string
        Player1 Player
        Player2 Player
    }
    
    // ignore this function contents, I grabbed it from elsewhere.
    func makeBoard() *Board {
        b := &Board{Tboard: [9]string{}}
        for x := 0; x < len(b.Tboard); x++ {
            b.Tboard[x] = "X"
            fmt.Println(b.Tboard[x])
        }
        b.Player1 = "George"
        b.Player2 = "Tim"
    
        fmt.Printf("Len: %v\n", len(b.Tboard)) // => 9
    
        fmt.Printf("Contents: %v\n", b)
        fmt.Printf("Syntax: %#v\n", b)
        fmt.Printf("Type: %T\n", b)
        fmt.Println("Board:", b.Tboard)
        return b
    }
    
    func main() {
        myBoard := makeBoard()
    
        v := reflect.ValueOf(*myBoard) // v is of type Value
        t := v.Type()
    
        fmt.Printf("Value: %v %T\n", v, v)
        fmt.Printf("Type:  %v %T\n", t, t)
    
        // would be a switch
        if t == reflect.TypeOf(*myBoard) {
            var b2 Board
    
            b2 = v.Interface().(Board) // cast the type interface{} into type Board
            fmt.Printf("v converted back to: %#v\n", b2)
        } else {
            fmt.Printf("t is not recognized")
        }
    
    }
    

    注意v 的类型是main.Board,完整的包名,而不是Board。 任何您想要执行此操作的结构都必须具有导出的类型才能使反射起作用。

    【讨论】:

    • 很好,这解决了我的大部分问题。所以现在我可以将reflect.Value 传递给我的DoTheThing 函数。但是我如何在DoTheThing 中将传递的reflect.Value 转换回它知道预期的底层*MyRandomComplicatedStructType
    猜你喜欢
    • 2013-02-28
    • 2010-11-25
    • 2014-03-31
    • 1970-01-01
    • 2021-02-01
    • 2012-07-06
    • 2012-11-07
    • 2014-08-13
    • 2014-10-12
    相关资源
    最近更新 更多