Parcourez les champs d'une structure dans Go

108

Fondamentalement, le seul moyen (que je connaisse) d'itérer les valeurs des champs de a structest comme ceci:

type Example struct {
    a_number uint32
    a_string string
}

//...

r := &Example{(2 << 31) - 1, "...."}:
for _, d:= range []interface{}{ r.a_number, r.a_string, } {
  //do something with the d
}

Je me demandais s'il y avait un moyen meilleur et plus polyvalent de réaliser []interface{}{ r.a_number, r.a_string, }, donc je n'ai pas besoin de lister chaque paramètre individuellement, ou alternativement, y a-t-il un meilleur moyen de boucler une structure?

J'ai essayé de regarder à travers le reflectpaquet, mais j'ai heurté un mur, car je ne sais pas quoi faire une fois que j'ai récupéré reflect.ValueOf(*r).Field(0).

Merci!

omninonsense
la source
5
Voici un article très intéressant concernant la réflexion: blog.golang.org/laws-of-reflection En suivant l'un des exemples de l'article: play.golang.org/p/_bKAQ3dQlu Notez cependant que vous ne pouvez pas rechercher les champs non exportés avec le package de réflexion (c.-à-d. champs commençant par des minuscules)
creack

Réponses:

126

Une fois que vous avez récupéré le reflect.Valuechamp à l'aide de, Field(i)vous pouvez en obtenir une valeur d'interface en appelant Interface(). Ladite valeur d'interface représente alors la valeur du champ.

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

Le plus proche que vous pouvez réaliser en aller est GetValue() interface{}et c'est exactement ce que reflect.Value.Interface() vous propose.

Le code suivant illustre comment obtenir les valeurs de chaque champ exporté dans une structure à l'aide de la réflexion ( lecture ):

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)
}
nemo
la source
24
Oui parce que go n'a pas besoin de génériques. Toux, toux :-) Y a-t-il un moyen d'obtenir le type de champ?
U Avalos
1
via reflect.Value.Type(), oui. Mais notez que les types ne sont pas des citoyens de première classe dans go, vous ne pouvez donc instancier que de nouvelles valeurs de ce type à l'aide de reflect.
nemo
7
v.Field(i).Interface()panique si vous essayez d'accéder aux champs privés non exportés. Faites attention :)
Tarion
10
En utiliser v.Field(i).CanInterface() un peut éviter la panique en cas de champs non exportés.
Pedram Esmaeeli
1
Comment puis-je obtenir le nom du champ?
Sathesh
33

Si vous souhaitez parcourir les champs et les valeurs d'une structure, vous pouvez utiliser le code Go ci-dessous comme 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("Field: %s\tValue: %v\n", typeOfS.Field(i).Name, v.Field(i).Interface())
    }
}

Courir dans la cour de récréation

Remarque: si les champs de votre structure ne sont pas exportés, la v.Field(i).Interface()panique apparaîtrapanic: reflect.Value.Interface: cannot return value obtained from unexported field or method.

Chetan Kumar
la source
0

Prendre une solution de Chetan Kumar et au cas où vous auriez besoinmap[string]int

package main

import (
    "fmt"
    "reflect"
)

type BaseStats struct {
    Hp           int
    HpMax        int
    Mp           int
    MpMax        int
    Strength     int
    Speed        int
    Intelligence int
}

type Stats struct {
    Base map[string]int
    Modifiers []string
}

func StatsCreate(stats BaseStats) Stats {
    s := Stats{
        Base: make(map[string]int),
    }

    //Iterate through the fields of a struct
    v := reflect.ValueOf(stats)
    typeOfS := v.Type()

    for i := 0; i< v.NumField(); i++ {
        val := v.Field(i).Interface().(int)
        s.Base[typeOfS.Field(i).Name] = val
    }
    return s
}

func (s Stats) GetBaseStat(id string) int {
    return s.Base[id]
}


func main() {
    m := StatsCreate(BaseStats{300, 300, 300, 300, 10, 10, 10})

    fmt.Println(m.GetBaseStat("Hp"))
}

ACIER
la source