web-dev-qa-db-fra.com

Accéder à la propriété struct par nom

Voici un programme simple qui ne fonctionne pas:

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

Erreur:

prog.go: 18: opération non valide: v [propriété] (index de type * sommet)

Ce que je veux, c'est accéder à la propriété Vertex X en utilisant son nom. Si je fais v.X ça marche, mais v["X"] ne le fait pas.

Quelqu'un peut-il me dire comment faire ce travail?

56
Nicolas BADIA

La plupart du code ne devrait pas avoir besoin de ce type de recherche dynamique. C'est inefficace comparé à l'accès direct (le compilateur connaît le décalage du champ X dans une structure Vertex, il peut compiler v.X en une seule instruction machine, alors qu'une recherche dynamique nécessitera une sorte d'implémentation de table de hachage ou similaire). Cela empêche également le typage statique: le compilateur n'a aucun moyen de vérifier que vous n'essayez pas d'accéder dynamiquement à des champs inconnus, et il ne peut pas savoir quel devrait être le type obtenu.

Mais ... le langage fournit un module reflet pour les rares fois où vous en avez besoin.

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

Il n'y a pas d'erreur de vérification ici. Vous aurez donc une panique si vous demandez un champ qui n'existe pas ou qui n'est pas de type int. Vérifiez la documentation de reflect pour plus de détails.

86
Paul Hankin

Vous avez maintenant le projet oleiade/reflections qui vous permet d'obtenir/de définir des champs sur des valeurs ou des pointeurs de structure.
Cela rend l'utilisation du package reflect moins compliquée.

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
10
VonC