Iteruj przez pola struktury w Go

108

Zasadniczo jedyny sposób (jaki znam) na iterację wartości pól a structjest taki:

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
}

Zastanawiałem się, czy istnieje lepszy i bardziej wszechstronny sposób osiągnięcia tego celu []interface{}{ r.a_number, r.a_string, }, więc nie muszę osobno wymieniać każdego parametru, czy też alternatywnie, czy istnieje lepszy sposób na zapętlenie przez strukturę?

Próbowałem przejrzeć reflectpaczkę, ale uderzyłem w ścianę, ponieważ nie jestem pewien, co zrobić po odzyskaniu reflect.ValueOf(*r).Field(0).

Dzięki!

omninonsense
źródło
5
Oto bardzo interesujący artykuł dotyczący refleksji: blog.golang.org/laws-of-reflection Podążając za jednym z przykładów z artykułu: play.golang.org/p/_bKAQ3dQlu Pamiętaj jednak, że nie możesz przeszukiwać nieeksportowanych pól z pakietem refleksji (tj. pól zaczynających się małymi literami)
zgrzytanie

Odpowiedzi:

126

Po pobraniu reflect.Valuepola za pomocą Field(i)można uzyskać z niego wartość interfejsu, wywołując Interface(). Wspomniana wartość interfejsu reprezentuje wówczas wartość pola.

Nie ma funkcji do konwersji wartości pola na konkretny typ, ponieważ, jak być może wiesz, nie ma żadnych typów ogólnych. Tak więc, nie ma funkcji z podpisem GetValue() T z Tbycia typem tej dziedzinie (która zmienia się oczywiście w zależności od dziedziny).

Najbliższe jest GetValue() interface{}to, co możesz osiągnąć w ruchu i właśnie to reflect.Value.Interface() oferuje.

Poniższy kod ilustruje, jak uzyskać wartości każdego eksportowanego pola w strukturze przy użyciu refleksji ( odtwarzania ):

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
źródło
24
Tak, ponieważ go nie potrzebuje leków generycznych. Kaszel, kaszel :-) Czy jest sposób na uzyskanie typu pola?
U Avalos
1
przez reflect.Value.Type(), tak. Należy jednak pamiętać, że typy nie są obywatelami pierwszej klasy w ruchu, więc nowe wystąpienia tego typu można tworzyć tylko przy użyciu reflect.
nemo
7
v.Field(i).Interface()paniki, jeśli spróbujesz uzyskać dostęp do niewyeksportowanych pól prywatnych. Tylko uważaj :)
Tarion
11
Korzystanie z niej v.Field(i).CanInterface() pozwala uniknąć paniki w przypadku niewyeksportowanych pól.
Pedram Esmaeeli
1
Jak mogę uzyskać nazwę pola?
Sathesh
33

Jeśli chcesz iterować przez pola i wartości struktury, możesz użyć poniższego kodu Go jako odniesienia.

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

Biegnij na placu zabaw

Uwaga: Jeśli pola w twojej strukturze nie są eksportowane, v.Field(i).Interface()wywoła panikępanic: reflect.Value.Interface: cannot return value obtained from unexported field or method.

Chetan Kumar
źródło
0

Przyjmowanie roztworu Chetan Kumar i na wypadek konieczności zastosowania się domap[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"))
}

STAL
źródło