185 votes

Parcourir les champs d'une structure en Go

Fondamentalement, la seule façon (que je connais) d'itérer à travers les valeurs des champs d'une struct est comme suit :

type Example struct {
    a_number uint32
    a_string string
}

//...

r := &Example{(2 << 31) - 1, "...."}:
for _, d:= range []interface{}{ r.a_number, r.a_string, } {
  //faire quelque chose avec le d
}

Je me demandais s'il y avait une meilleure et plus polyvalente façon d'obtenir []interface{}{ r.a_number, r.a_string, }, pour ne pas avoir à lister chaque paramètre individuellement, ou bien, s'il y avait une meilleure façon de boucler à travers une struct ?

J'ai essayé de parcourir le reflect package, mais j'ai rencontré un obstacle, car je ne sais pas quoi faire une fois que j'ai obtenu reflect.ValueOf(*r).Field(0).

Merci !

178voto

nemo Points 13983

Après avoir récupéré la reflect.Value du champ en utilisant Field(i), vous pouvez obtenir une valeur d'interface en appelant Interface(). Cette valeur d'interface représente ensuite la valeur du champ.

Il n'existe pas de fonction pour convertir la valeur du champ en un type concret car, comme vous le savez peut-être, il n'y a pas de génériques en go. Ainsi, il n'existe pas de fonction avec la signature GetValue() T avec T étant le type de ce champ (qui change bien sûr, en fonction du champ).

Le plus proche que vous pouvez obtenir en go est GetValue() interface{} et c'est exactement ce que reflect.Value.Interface() offre.

Le code suivant illustre comment obtenir les valeurs de chaque champ exporté dans une structure en utilisant la réflexion (play):

import (
    "fmt"
    "reflect"
)

func main() {
    x := struct{Foo string; Bar int }{"foo", 2}

    v := reflect.ValueOf(x)

    values := make([]interface{}, v.NumField())

    for i := 0; i < v.NumField(); i++ {
        values[i] = v.Field(i).Interface()
    }

    fmt.Println(values)
}

102voto

Chetan Kumar Points 723

Si vous souhaitez itérer à travers les champs et les valeurs d'une structure, vous pouvez utiliser le code Go ci-dessous à titre de référence.

package main

import (
    "fmt"
    "reflect"
)

type Student struct {
    Fname  string
    Lname  string
    City   string
    Mobile int64
}

func main() {
    s := Student{"Chetan", "Kumar", "Bangalore", 7777777777}
    v := reflect.ValueOf(s)
    typeOfS := v.Type()

    for i := 0; i< v.NumField(); i++ {
        fmt.Printf("Champ: %s\tValeur: %v\n", typeOfS.Field(i).Name, v.Field(i).Interface())
    }
}

Exécutez-le dans l'aire de jeux

Note : Si les champs de votre structure ne sont pas exportés, alors v.Field(i).Interface() provoquera une panique panic: reflect.Value.Interface: cannot return value obtained from unexported field or method.

34voto

VonC Points 414372

Go 1.17 (T3 2021) devrait ajouter une nouvelle option, via commit 009bfea et CL 281233, corrigeant problème 42782.

reflect : ajouter la fonction VisibleFields

Lors de l'écriture de code qui reflète un type de struct, il est courant de connaître l'ensemble complet des champs de struct, y compris les champs disponibles en raison de l'intégration de membres anonymes en excluant les champs qui sont effacés parce qu'ils sont au même niveau qu'un autre champ portant le même nom.

La logique pour faire cela n'est pas si complexe, mais c'est un peu subtil et facile à mal interpréter.

Ce CL ajoute une nouvelle fonction reflect.VisibleFields() au package reflect qui renvoie l'ensemble complet de champs effectifs qui s'appliquent dans un type de struct donné.

fields := reflect.VisibleFields(typ)
for j, field := range fields {
    ...
}

Exemple,

type detailsEmploye struct {
    id          int16
    nom         string
    designation string
}
func iteratorDeStruct() {
    fields := reflect.VisibleFields(reflect.TypeOf(struct{ detailsEmploye }{}))
    for _, field := range fields {
        fmt.Printf("Clé : %s\tType : %s\n", field.Name, field.Type)
    }
}

7voto

Esmaeil Abedi Points 79

Peut-être trop tard :))) mais il y a une autre solution que vous pouvez trouver la clé et la valeur des structs et itérer dessus

package main

import (
    "fmt"
    "reflect"
)

type person struct {
    firsName string
    lastName string
    iceCream []string
}

func main() {
    u := struct {
        myMap    map[int]int
        mySlice  []string
        myPerson person
    }{
        myMap:   map[int]int{1: 10, 2: 20},
        mySlice: []string{"red", "green"},
        myPerson: person{
            firsName: "Esmaeil",
            lastName: "Abedi",
            iceCream: []string{"Vanilla", "chocolate"},
        },
    }
    v := reflect.ValueOf(u)
    for i := 0; i < v.NumField(); i++ {
        fmt.Println(v.Type().Field(i).Name)
        fmt.Println("\t", v.Field(i))
    }
}

et il n'y a pas de *panic* pour v.Field(i)

0voto

mahdi gadget Points 83

Utilisez ceci :

type x struct {
    Id  int
    jsj int
}
func main() {
    x2 := x{jsj: 10, Id: 5}
    v := reflect.ValueOf(x2)
    for i := 0; i < v.NumField(); i++ {
        fmt.Println(v.Field(i))
    }
}

\====>10

\====>5

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X