range over interface {} qui stocke une tranche

96

Compte tenu du scénario où vous avez une fonction qui accepte t interface{}. S'il est déterminé que le test une tranche, comment puis-je rangesur cette tranche?

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

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

Nucléon
la source
Pourquoi ne pas utiliser une [] interface {} à la place? Essayez-vous de gérer plusieurs types complexes?
Jeremy Wall
2
Oui, c'est pour un système de modèles, donc l'interface {} peut être une carte, une structure, une tranche ou un tableau. Dans le vrai code, il y a beaucoup plus d'énoncés de cas, mais je les ai supprimés du message pour rendre le problème plus concis.
Nucleon
1
Jeremy, une chaîne [] n'est pas un sous-type de [] interface {}, donc vous ne pouvez pas appeler une fonction func ([] interface {}) avec une chaîne [] ou [] int, etc. Ce serait bien de avoir une fonctionnalité dans Go pour avoir un type signifiant "tranche de quelque chose", où vous pouvez ensuite parcourir les éléments comme interface {}, mais malheureusement vous avez besoin de réflexion pour cela.
Bjarke Ebert
@JeremyWall Vous ne pouvez pas utiliser [] interface {} pour agir comme tranche. Vous pouvez vous référer ici .
firelyu

Réponses:

137

Eh bien, j'ai utilisé reflect.ValueOfet puis si c'est une tranche, vous pouvez appeler Len()et Index()sur la valeur pour obtenir lenla tranche et l'élément à un index. Je ne pense pas que vous pourrez utiliser la gamme pour faire cela.

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

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

masebase
la source
23
Fonctionne très bien, merci. La seule chose à ajouter serait que s.Index(i)renvoie un reflect.Valuedonc dans mon cas, j'avais besoin s.Index(i).Interface()de référencer la valeur réelle.
Nucleon
1
Que feriez-vous si vous aviez un pointeur vers une tranche interface{}? eg moredata := &[]int{1,2,3}
Ryan Walls
1
Réponse à ma question: si vous avez un pointeur vers une tranche au lieu d'une tranche, vous devrez l'utiliser Elem()pour obtenir la valeur sous-jacente. eg reflect.TypeOf(reflect.ValueOf(t).Elem().Interface()).Kind()
Ryan Walls
que faire si je veux obtenir la valeur du champ d'interface à partir d'une tranche d'interface sans aucune structure. J'ai essayé cette solution mais elle est limitée pour obtenir la référence d'interface uniquement à partir de la tranche et non des valeurs réelles des champs.
Amandeep kaur le
woh merci beaucoup, cette astuce m'a fait gagner beaucoup de temps (et mal de tête!)
Nicolas Garnier
26

Vous n'avez pas besoin d'utiliser la réflexion si vous savez à quels types vous attendre. Vous pouvez utiliser un commutateur de type , comme ceci:

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

Consultez le code sur le terrain de jeu .

Inanc Gumus
la source
Cela ne fonctionnera pas sur un tableau, mais uniquement sur une tranche
user1028741
@ user1028741 Non, le code fonctionne pour tous les types, y compris les tableaux + Vous pouvez toujours prendre une tranche d'un tableau array[:].
Inanc Gumus le
Mais le type d'un tableau dépendra de sa capacité réelle. [3] int n'est pas du même type que [2] int ou [] int. Par conséquent, si le type de «t» est en fait un tableau, le cas pertinent ne s'appliquera pas.
user1028741
Ce que je veux dire, c'est que votre exemple ne fonctionnera pas sur les tableaux. Je l'ai changé ici: play.golang.org/p/DAsg_0aXz-r
user1028741
Vous ne pouvez pas facilement le faire fonctionner, si vous ne connaissez pas le type de votre paramètre en premier lieu. Afin d'utiliser votre astuce (tableau [:]), vous devrez utiliser la réflexion pour décider qu'il s'agit d'un tableau, puis transtyper en tableau - puis en générer une tranche. C'est pourquoi j'ai dit que cela fonctionne sur une tranche, pas sur un tableau. De toute évidence, avec suffisamment d'effort, vous pouvez produire une tranche hors du tableau ...
user1028741
4

il y a une exception à la façon dont l'interface {} se comporte, @Jeremy Wall a déjà donné un pointeur. si les données passées sont définies comme [] interface {} initialement.

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

production:

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]

Essaye le

Matus Kral
la source