Bereich über Schnittstelle {}, in der ein Slice gespeichert ist

95

In Anbetracht des Szenarios, in dem Sie eine Funktion haben, die akzeptiert t interface{}. Wenn festgestellt wird, dass es sich um tein Slice handelt, wie gehe ich dann rangeüber dieses Slice hinaus?

func main() {
    data := []string{"one","two","three"}
    test(data)
    moredata := []int{1,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)
        }
    }
}

Go Playground Beispiel: http://play.golang.org/p/DNldAlNShB

Nucleon
quelle
Warum sollte die Funktion nicht stattdessen eine [] Schnittstelle {} verwenden? Versuchen Sie, mit mehreren komplexen Typen umzugehen?
Jeremy Wall
2
Ja, es handelt sich um ein Template-System, sodass die Schnittstelle {} eine Map, Struktur, ein Slice oder ein Array sein kann. Im realen Code gibt es viel mehr case-Anweisungen, aber ich habe sie aus dem Beitrag entfernt, um das Problem präziser zu gestalten.
Nucleon
1
Jeremy, ein [] String ist kein Subtyp von [] interface {}, daher kann man eine func ([] interface {}) - Funktion nicht mit einem [] String oder [] int usw. aufrufen haben eine Funktion in Go, um einen Typ zu haben, der "Slice of Something" bedeutet, wo Sie dann als Schnittstelle {} über die Elemente iterieren können, aber leider müssen Sie dafür reflektieren.
Bjarke Ebert
@JeremyWall Sie können die [] -Schnittstelle {} nicht als Slice verwenden. Sie können hier verweisen .
Firelyu

Antworten:

136

Nun, ich habe verwendet reflect.ValueOfund dann, wenn es ein Slice ist, können Sie Len()und Index()auf den Wert aufrufen , um lendas Slice und Element an einem Index zu erhalten. Ich glaube nicht, dass Sie die Reichweite verwenden können, um dies zu tun.

package main

import "fmt"
import "reflect"

func main() {
    data := []string{"one","two","three"}
    test(data)
    moredata := []int{1,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))
        }
    }
}

Go Playground Beispiel: http://play.golang.org/p/gQhCTiwPAq

masebase
quelle
23
Funktioniert super, danke. Das einzige, was hinzugefügt werden muss, ist, dass s.Index(i)a zurückgegeben wird. reflect.ValueIn meinem Fall musste ich s.Index(i).Interface()auf den tatsächlichen Wert verweisen.
Nucleon
1
Was würden Sie tun, wenn Sie einen Zeiger auf ein Slice in hätten interface{}? zB moredata := &[]int{1,2,3}
Ryan Walls
1
Beantwortung meiner Frage: Wenn Sie einen Zeiger auf ein Slice anstelle eines Slice haben, müssen Sie diesen verwenden Elem(), um den zugrunde liegenden Wert zu erhalten. zB reflect.TypeOf(reflect.ValueOf(t).Elem().Interface()).Kind()
Ryan Walls
Was ist, wenn ich den Wert des Schnittstellenfelds von einem Schnittstellenschnitt ohne Struktur erhalten möchte? Ich habe diese Lösung ausprobiert, aber es ist beschränkt, die Schnittstellenreferenz nur vom Slice abzurufen, nicht von den tatsächlichen Werten der Felder.
Amandeep Kaur
Vielen Dank, dieser Trick hat mir viel Zeit (und Kopfschmerzen!) gespart
Nicolas Garnier
25

Sie müssen keine Reflexion verwenden, wenn Sie wissen, welche Typen zu erwarten sind. Sie können einen Typschalter wie folgt verwenden:

package main

import "fmt"

func main() {
    loop([]string{"one", "two", "three"})
    loop([]int{1, 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)
        }
    }
}

Überprüfen Sie den Code auf dem Spielplatz .

Inanc Gumus
quelle
Dies funktioniert jedoch nicht über ein Array,
sondern
@ user1028741 Nein, der Code funktioniert für jeden Typ, einschließlich Arrays. + Sie können jederzeit ein Slice aus einem Array entnehmen array[:].
Inanc Gumus
Der Typ eines Arrays hängt jedoch von der tatsächlichen Kapazität ab. [3] int ist nicht vom gleichen Typ wie [2] int oder [] int. Wenn der Typ von 't' tatsächlich ein Array ist, gilt der relevante Fall daher nicht.
user1028741
Ich meine, Ihr Beispiel funktioniert nicht mit Arrays. Ich habe es hier geändert: play.golang.org/p/DAsg_0aXz-r
user1028741
Sie können es nicht einfach zum Laufen bringen, wenn Sie den Typ Ihres Parameters überhaupt nicht kennen. Um Ihren Trick (Array [:]) zu verwenden, müssen Sie mithilfe der Reflexion entscheiden, dass es sich um ein Array handelt, dann in ein Array umwandeln und dann ein Slice davon generieren. Deshalb habe ich gesagt, dass es auf einem Slice funktioniert, nicht auf einem Array.
Natürlich
4

Es gibt eine Ausnahme von der Art und Weise, wie sich die Schnittstelle {} verhält. @Jeremy Wall hat bereits einen Zeiger gegeben. wenn die übergebenen Daten anfänglich als [] Schnittstelle {} definiert sind.

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]string{3: "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)
        }
    }
}

Ausgabe:

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]

Versuch es

Matus Kral
quelle