Greifen Sie über den Namen auf die struct-Eigenschaft zu

75

Hier ist ein einfaches Go-Programm, das nicht funktioniert:

package main
import "fmt"

type Vertex struct {
    X int
    Y int
}

func main() {
    v := Vertex{1, 2}
    fmt.Println(getProperty(&v, "X"))
}

func getProperty(v *Vertex, property string) (string) {
    return v[property]
}

Error:

prog.go: 18: ungültige Operation: v [Eigenschaft] (Index vom Typ * Vertex)

Ich möchte auf die Vertex X-Eigenschaft mit ihrem Namen zugreifen. Wenn ich es tue v.X, funktioniert es, aber v["X"]nicht.

Kann mir jemand sagen, wie das funktioniert?

Nicolas BADIA
quelle

Antworten:

116

Der meiste Code sollte diese Art der dynamischen Suche nicht benötigen. Es ist ineffizient im Vergleich zum direkten Zugriff (der Compiler kennt den Versatz des X-Felds in einer Vertex-Struktur, er kann vX zu einer einzelnen Maschinenanweisung kompilieren, während eine dynamische Suche eine Art Hash-Tabellenimplementierung oder ähnliches erfordert). Es verhindert auch die statische Typisierung: Der Compiler kann nicht überprüfen, ob Sie nicht versuchen, dynamisch auf unbekannte Felder zuzugreifen, und er kann nicht wissen, wie der resultierende Typ aussehen soll.

Aber ... die Sprache bietet ein Reflektionsmodul für die seltenen Fälle, in denen Sie dies benötigen.

package main

import "fmt"
import "reflect"

type Vertex struct {
    X int
    Y int
}

func main() {
    v := Vertex{1, 2}
    fmt.Println(getField(&v, "X"))
}

func getField(v *Vertex, field string) int {
    r := reflect.ValueOf(v)
    f := reflect.Indirect(r).FieldByName(field)
    return int(f.Int())
}

Hier gibt es keine Fehlerprüfung, sodass Sie in Panik geraten, wenn Sie nach einem Feld fragen, das nicht vorhanden ist, oder wenn das Feld nicht vom Typ int ist. Weitere Informationen finden Sie in der Dokumentation .

Paul Hankin
quelle
2
+1, und siehe auch Die Gesetze der Reflexion, die eine Einführung in die Idee geben.
Fred Foo
1
Dieses Reflect-Modul ist etwas knifflig. Ich habe versucht, es ohne Erfolg zu verwenden. Es scheint, dass ich vergessen habe anzurufen Ìndirect. Vielen Dank für das Arbeitsbeispiel und die Erklärung. Wirklich zu schätzen :-)
Nicolas BADIA
2
Vielen Dank für die Erklärung über dem Code. Für mich ist es sogar noch nützlicher als der Code selbst!
Nebulosar
13

Sie haben jetzt das Projekt Oleiade / Reflections , mit dem Sie Felder für Strukturwerte oder Zeiger abrufen / festlegen können.
Dies macht die Verwendung des reflectPakets weniger schwierig.

s := MyStruct {
    FirstField: "first value",
    SecondField: 2,
    ThirdField: "third value",
}

fieldsToExtract := []string{"FirstField", "ThirdField"}

for _, fieldName := range fieldsToExtract {
    value, err := reflections.GetField(s, fieldName)
    DoWhatEverWithThatValue(value)
}


// In order to be able to set the structure's values,
// a pointer to it has to be passed to it.
_ := reflections.SetField(&s, "FirstField", "new value")

// If you try to set a field's value using the wrong type,
// an error will be returned
err := reflection.SetField(&s, "FirstField", 123)  // err != nil
VonC
quelle
0

Sie können die Struktur marshallen und zurück zu marshallen map[string]interface{}. Es werden jedoch alle Zahlenwerte float64in konvertiert , sodass Sie sie intmanuell konvertieren müssen.

type Vertex struct {
    X int
    Y int
}

func main() {
    v := Vertex{1, 2}
    fmt.Println(getProperty(&v, "X"))
}

func getProperty(v *Vertex, property string) float64 {
    m, _ := json.Marshal(v)
    var x map[string]interface{}
    _ = json.Unmarshal(m, &x)
    return x[property].(float64)
}
Deepak Sah
quelle