2013-09-21 11 views
38

Hier ist ein einfaches Programm gehen, das nicht funktioniert:golang: Zugriff struct Eigenschaft mit Namen

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

Fehler: prog.go:18: invalid operation: v[property] (index of type *Vertex)

Was ich will, das Vertex X Eigenschaft mit seinem Namen zuzugreifen ist. Wenn ich v.X tun, funktioniert es, aber v["X"] nicht.

Kann mir jemand sagen, wie man das macht?

Antwort

56

Die meisten Code sollte diese Art der dynamischen Suche nicht benötigen. Es ist ineffizient im Vergleich zum direkten Zugriff (der Compiler kennt den Offset des X-Feldes in einer Vertex-Struktur, er kann v.X zu einem einzelnen Maschinenbefehl kompilieren, während ein dynamischer Lookup eine Art Hash-Tabellenimplementierung oder ähnliches benötigt). Es verhindert auch die statische Typisierung: Der Compiler hat keine Möglichkeit zu überprüfen, dass Sie nicht versuchen, auf unbekannte Felder dynamisch zuzugreifen, und er kann nicht wissen, was der resultierende Typ sein soll.

Aber ... die Sprache bietet ein reflect Modul für die seltenen Zeiten, die Sie 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()) 
} 

Es gibt keinen Fehler hier überprüft, so dass Sie eine Panik, wenn man für ein Feld stellen, die nicht vorhanden ist, oder das Feld nicht vom Typ int. Überprüfen Sie the documentation for reflect für weitere Details.

+2

+1, und siehe auch [Die Gesetze der Reflexion] (http://blog.golang.org/laws-of-reflection), die eine Einführung in die Idee gibt. –

+1

Dieses Reflektormodul ist ziemlich kompliziert. Ich habe versucht, es ohne Erfolg zu verwenden. Es scheint, dass ich vergessen habe, 'Ìndirect' zu nennen. Danke für das Arbeitsbeispiel und die ganze Erklärung. Wirklich zu schätzen :-) –

+1

Danke für die Erklärung über den Code. Für mich ist es sogar noch nützlicher als der Code selbst! – Nebulosar

6

Jetzt haben Sie das Projekt oleiade/reflections, mit dem Sie Felder auf Strukturwert oder Zeiger erhalten/setzen können.
Es macht die reflect package weniger knifflig.

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