Scorri i campi di una struttura in Go


107

Fondamentalmente, l'unico modo (che io conosca) per iterare attraverso i valori dei campi di a structè così:

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
}

Mi chiedevo, se esiste un modo migliore e più versatile per ottenere risultati []interface{}{ r.a_number, r.a_string, }, quindi non ho bisogno di elencare ogni parametro individualmente o, in alternativa, c'è un modo migliore per scorrere una struttura?

Ho provato a guardare attraverso il reflectpacchetto, ma ho sbattuto contro un muro, perché non sono sicuro di cosa fare una volta recuperato reflect.ValueOf(*r).Field(0).

Grazie!


5
Ecco un articolo molto interessante sulla riflessione: blog.golang.org/laws-of-reflection Seguendo uno degli esempi dell'articolo: play.golang.org/p/_bKAQ3dQlu Nota tuttavia che non puoi cercare campi non esportati con il pacchetto di riflessione (cioè i campi che iniziano con
lettere

Risposte:


126

Dopo aver recuperato il reflect.Valuedel campo utilizzando Field(i)è possibile ottenere un valore di interfaccia da esso chiamando Interface(). Detto valore di interfaccia rappresenta quindi il valore del campo.

Non esiste una funzione per convertire il valore del campo in un tipo concreto in quanto, come forse saprai, non ci sono generici in corso. Pertanto, non esiste alcuna funzione con la firma GetValue() T con Til tipo di quel campo (che cambia ovviamente, a seconda del campo).

Il massimo che puoi ottenere in go è GetValue() interface{}e questo è esattamente ciò che reflect.Value.Interface() offre.

Il codice seguente illustra come ottenere i valori di ogni campo esportato in una struttura utilizzando la riflessione ( riproduzione ):

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

24
Sì, perché andare non ha bisogno di farmaci generici. Tosse, tosse :-) C'è un modo per ottenere il tipo di campo?
U Avalos

1
via reflect.Value.Type(), sì. Ma nota che i tipi non sono cittadini di prima classe in movimento, quindi puoi solo istanziare nuovi valori di quel tipo usando reflect.
nemo

6
v.Field(i).Interface()panico se si tenta di accedere a campi privati ​​non esportati.
Fai

10
Utilizzando v.Field(i).CanInterface() uno può evitare il panico in caso di campi non esportate.
Pedram Esmaeeli

1
Come posso ottenere il nome del campo?
Sathesh

33

Se vuoi scorrere i campi e i valori di una struttura, puoi utilizzare il codice Go di seguito come riferimento.

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

Corri nel parco giochi

Nota: se i campi nella tua struttura non vengono esportati v.Field(i).Interface(), darà il panicopanic: reflect.Value.Interface: cannot return value obtained from unexported field or method.


0

Prendendo la soluzione di Chetan Kumar e nel caso in cui sia necessario rivolgersi a unmap[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"))
}

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.