Durchlaufen Sie die Felder einer Struktur in Go

107

Grundsätzlich ist die einzige Möglichkeit (die ich kenne), die Werte der Felder von a zu durchlaufen, structwie folgt:

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
}

Ich habe mich gefragt, ob es einen besseren und vielseitigeren Weg gibt []interface{}{ r.a_number, r.a_string, }, um dies zu erreichen. Ich muss also nicht jeden Parameter einzeln auflisten, oder gibt es alternativ einen besseren Weg, um eine Struktur zu durchlaufen?

Ich habe versucht, das reflectPaket durchzusehen , bin aber gegen eine Wand gestoßen, weil ich nicht sicher bin, was ich tun soll, wenn ich es abrufe reflect.ValueOf(*r).Field(0).

Vielen Dank!

Omninonsense
quelle
5
Hier ist ein sehr interessanter Artikel zum Thema Reflexion: blog.golang.org/laws-of-reflection Nach einem Beispiel aus dem Artikel: play.golang.org/p/_bKAQ3dQlu Beachten Sie jedoch, dass Sie nicht exportierte Felder nicht suchen können mit dem
Reflect-

Antworten:

125

Nachdem Sie reflect.Valuedas Feld mithilfe von abgerufen haben Field(i), können Sie durch Aufrufen einen Schnittstellenwert daraus abrufen Interface(). Dieser Schnittstellenwert repräsentiert dann den Wert des Feldes.

Es gibt keine Funktion zum Konvertieren des Feldwerts in einen konkreten Typ, da, wie Sie vielleicht wissen, keine Generika vorhanden sind. Somit gibt es keine Funktion mit der Signatur GetValue() T mit Twobei der Typ des Feldes (das natürlich ändert, auf dem Feld abhängig).

Das nächste, was Sie in go erreichen können, ist GetValue() interface{}und genau das reflect.Value.Interface() bietet.

Der folgende Code veranschaulicht, wie die Werte jedes exportierten Felds in einer Struktur mithilfe von Reflection ( Play ) abgerufen werden :

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
quelle
23
Ja, weil go keine Generika braucht. Husten, Husten :-) Gibt es eine Möglichkeit, die Art des Feldes zu ermitteln?
U Avalos
via reflect.Value.Type()ja. Beachten Sie jedoch, dass Typen keine erstklassigen Bürger sind, sodass Sie neue Werte dieses Typs nur mit instanziieren können reflect.
Nemo
6
v.Field(i).Interface()Panik, wenn Sie versuchen, auf nicht exportierte private Felder zuzugreifen. Sei einfach vorsichtig :)
Tarion
10
Die Verwendung von v.Field(i).CanInterface() One kann die Panik bei nicht exportierten Feldern vermeiden.
Pedram Esmaeeli
1
Wie kann ich den Feldnamen erhalten?
Sathesh
33

Wenn Sie durch die Felder und Werte einer Struktur iterieren möchten, können Sie den folgenden Go-Code als Referenz verwenden.

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

Lauf auf dem Spielplatz

Hinweis: Wenn die Felder in Ihrer Struktur nicht exportiert v.Field(i).Interface()werden, kommt es zu Panikpanic: reflect.Value.Interface: cannot return value obtained from unexported field or method.

Chetan Kumar
quelle
0

Nehmen Sie die Lösung von Chetan Kumar und falls Sie sich bei a bewerben müssenmap[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"))
}

STAHL
quelle