遍历接口

huangapple go评论83阅读模式
英文:

Iterate over an interface

问题

我想创建一个函数,它接受一个地图或任何类型的数组,并在每个项目上调用一个函数,该函数知道如何处理遇到的任何类型。

这是我第一次失败的尝试。目前,在我的实际用例中运行时,它总是显示"uh oh!"。

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中迭代数组或地图的方法?

英文:

I want to create a function that takes either a map or an array of whatever and iterates over it calling a function on each item which knows what to do with whatever types it encounters.

Here's my first failed attempt. Currently when I run it in my real use case it always says "uh oh!".

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
}

The array or map could contain lots of different things so it isn't an option to try and match every possible input.

Stated otherwise, is there a way to iterate over an array or map in Go without knowing exactly what it is?

答案1

得分: 1

为了使你的示例工作,你需要构建一个interface{}的数组(或映射),以便正确的类型被检测到:

// 这样是行不通的,因为.(type)会被识别为[]S
someS := []S{S{}}
DoTheThingToAllTheThings(someS)

// 这样可以:它会进入case []interface{}:
someSI := make([]interface{}, len(someS))
for i, s := range someS {
    someSI[i] = s
}
DoTheThingToAllTheThings(someSI)

在你的DoTheThing函数中,这意味着你仍然需要使用interface{}进行操作。正如我在"What would generics in Go be?"中提到的,这里没有真正的泛型。

你可以在这里看到完整的示例。

英文:

For your example to work, you would need to build an array of interface{} (or a map), in order for the right type to be detected:

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

See a full example here.

But that means you would still work with interface{} in your DoTheThing function.
There is no real generic here, as I mentioned in "What would generics in Go be?".

答案2

得分: 1

函数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

它使用了"reflect"包:http://golang.org/pkg/reflect/来查询参数的类型。在这里的p.printReflectValue中:http://golang.org/src/pkg/fmt/print.go?h=printArg#L862,你将看到处理映射和结构的两种情况。然后它通过printValue递归处理内容。

下面是一段代码,它获取一个结构的反射,然后将其转换回正确类型的另一个变量。你不能使用这种方法从一个任意类型转换为另一个任意类型,转换必须在Go语言中是合法的,而不使用反射。

package main

import (
	"fmt"
	"reflect"
)

type Player string

type Board struct {
	Tboard  [9]string
	Player1 Player
	Player2 Player
}

// 忽略这个函数的内容,我从其他地方复制过来的。
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 是 Value 类型
	t := v.Type()

	fmt.Printf("Value: %v %T\n", v, v)
	fmt.Printf("Type:  %v %T\n", t, t)

	// 这里应该是一个 switch 语句
	if t == reflect.TypeOf(*myBoard) {
		var b2 Board

		b2 = v.Interface().(Board) // 将类型 interface{} 转换为类型 Board
		fmt.Printf("v converted back to: %#v\n", b2)
	} else {
		fmt.Printf("t is not recognized")
	}

}

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

英文:

The function fmt.Printf(&quot;%v\n&quot;, data_interface) does exactly what you want. It will print the whole map or array passed to it.

You can find the implementation here: http://golang.org/src/pkg/fmt/print.go?h=printArg#L708

The line near the end of printArg is key:

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

It's using the "reflect" package: http://golang.org/pkg/reflect/ to query the type of the argument. Inside p.printReflectValue here: http://golang.org/src/pkg/fmt/print.go?h=printArg#L862 You will see the two cases where maps and structures are dealt with. It then uses recursion through printValue to manage the contents.

Here's a bit of code that takes the reflection of a structure and then turns it back into another variable of the right type. You can't change from one arbitrary type to another using this, it has to be legal in go to cast the types from one to another without using reflection.

package main

import (
	&quot;fmt&quot;
	&quot;reflect&quot;
)

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 := &amp;Board{Tboard: [9]string{}}
	for x := 0; x &lt; len(b.Tboard); x++ {
		b.Tboard[x] = &quot;X&quot;
		fmt.Println(b.Tboard[x])
	}
	b.Player1 = &quot;George&quot;
	b.Player2 = &quot;Tim&quot;

	fmt.Printf(&quot;Len: %v\n&quot;, len(b.Tboard)) // =&gt; 9

	fmt.Printf(&quot;Contents: %v\n&quot;, b)
	fmt.Printf(&quot;Syntax: %#v\n&quot;, b)
	fmt.Printf(&quot;Type: %T\n&quot;, b)
	fmt.Println(&quot;Board:&quot;, b.Tboard)
	return b
}

func main() {
	myBoard := makeBoard()

	v := reflect.ValueOf(*myBoard) // v is of type Value
	t := v.Type()

	fmt.Printf(&quot;Value: %v %T\n&quot;, v, v)
	fmt.Printf(&quot;Type:  %v %T\n&quot;, 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(&quot;v converted back to: %#v\n&quot;, b2)
	} else {
		fmt.Printf(&quot;t is not recognized&quot;)
	}

}

Notice that the type of v is main.Board, the full package name, not Board.
Any structure you want to do this to, must have an exported type for reflection to work.

huangapple
  • 本文由 发表于 2014年9月13日 17:17:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/25821708.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定