En utilisant Reflect, comment définir la valeur d'un champ struct?

106

avoir du mal à travailler avec des champs struct en utilisant reflectpackage. en particulier, n'ont pas compris comment définir la valeur du champ.

tapez t struct {fi int; chaîne fs}
var rt = t {123, "jblow"}
var i64 int64 = 456
  1. obtenir le nom du champ i - cela semble fonctionner

    var field = reflect.TypeOf(r).Field(i).Name

  2. obtenir la valeur du champ i comme a) interface {}, b) int - cela semble fonctionner

    var iface interface{} = reflect.ValueOf(r).Field(i).Interface()

    var i int = int(reflect.ValueOf(r).Field(i).Int())

  3. réglage de la valeur du champ i - essayer un - panique

    reflect.ValueOf(r).Field(i).SetInt( i64 )

    panique : reflect.Value · SetInt en utilisant la valeur obtenue en utilisant un champ non exporté

    en supposant qu'il n'aime pas les noms de champ "id" et "nom", alors renommé en "Id" et "Nom"

    a) cette hypothèse est-elle correcte?

    b) si correct, jugé inutile car dans le même fichier / package

  4. réglage de la valeur du champ i - essayez deux (avec les noms de champ en majuscule) - panique

    reflect.ValueOf(r).Field(i).SetInt( 465 )

    reflect.ValueOf(r).Field(i).SetInt( i64 )

    panique : reflect.Value · SetInt utilisant une valeur non adressable


Les instructions ci-dessous par @peterSO sont complètes et de haute qualité

Quatre. cela marche:

reflect.ValueOf(&r).Elem().Field(i).SetInt( i64 )

il atteste également que les noms de champs doivent être exportables (commencer par une majuscule)

cc jeune
la source
L'exemple le plus proche que j'ai pu trouver pour quelqu'un utilisant reflectpour définir des données était comments.gmane.org/gmane.comp.lang.go.general/35045 , mais même là, il faisait json.Unmarshalle sale boulot
cc young
(le commentaire ci-dessus est obsolète)
cc young

Réponses:

156

Go est disponible en code open source . Un bon moyen d'en apprendre davantage sur la réflexion est de voir comment les principaux développeurs de Go l'utilisent. Par exemple, les packages Go fmt et json . La documentation du package contient des liens vers les fichiers de code source sous la rubrique Fichiers du package.

Le package Go json rassemble et annule le JSON depuis et vers les structures Go.


Voici un exemple étape par étape qui définit la valeur d'un structchamp tout en évitant soigneusement les erreurs.

Le reflectpackage Go a une CanAddrfonction.

func (v Value) CanAddr() bool

CanAddr renvoie true si l'adresse de la valeur peut être obtenue avec Addr. Ces valeurs sont appelées adressables. Une valeur est adressable s'il s'agit d'un élément d'une tranche, d'un élément d'un tableau adressable, d'un champ d'une structure adressable ou du résultat du déréférencement d'un pointeur. Si CanAddr renvoie false, appeler Addr paniquera.

Le reflectpackage Go a une CanSetfonction qui, si true, implique que CanAddrc'est aussi true.

func (v Value) CanSet() bool

CanSet renvoie true si la valeur de v peut être modifiée. Une valeur ne peut être modifiée que si elle est adressable et n'a pas été obtenue par l'utilisation de champs struct non exportés. Si CanSet renvoie false, l'appel de Set ou de tout autre setter spécifique au type (par exemple, SetBool, SetInt64) paniquera.

Nous devons nous assurer que nous pouvons Setle structterrain. Par exemple,

package main

import (
    "fmt"
    "reflect"
)

func main() {
    type t struct {
        N int
    }
    var n = t{42}
    // N at start
    fmt.Println(n.N)
    // pointer to struct - addressable
    ps := reflect.ValueOf(&n)
    // struct
    s := ps.Elem()
    if s.Kind() == reflect.Struct {
        // exported field
        f := s.FieldByName("N")
        if f.IsValid() {
            // A Value can be changed only if it is 
            // addressable and was not obtained by 
            // the use of unexported struct fields.
            if f.CanSet() {
                // change value of N
                if f.Kind() == reflect.Int {
                    x := int64(7)
                    if !f.OverflowInt(x) {
                        f.SetInt(x)
                    }
                }
            }
        }
    }
    // N at end
    fmt.Println(n.N)
}

Output:
42
7

Si nous pouvons être certains que toutes les vérifications d'erreurs ne sont pas nécessaires, l'exemple se simplifie en,

package main

import (
    "fmt"
    "reflect"
)

func main() {
    type t struct {
        N int
    }
    var n = t{42}
    fmt.Println(n.N)
    reflect.ValueOf(&n).Elem().FieldByName("N").SetInt(7)
    fmt.Println(n.N)
}
peterSO
la source
5
Abandonner! quelque part il y a une réponse, mais quatre heures de travail sur le pkg json ne me l'ont pas cédé. En ce qui concerne le paquet de réflexion, extraire des informations est assez simple, mais la configuration des données nécessite une partie de la magie noire pour laquelle j'aimerais voir un exemple simple quelque part!
cc young
2
Exceptionnel! si jamais vous êtes en Thaïlande, laissez-moi vous offrir une bière ou deux ou trois! merci beaucoup
cc young
5
Excellent exemple pratique, cet article l'a complètement démystifié pour moi golang.org/doc/articles/laws_of_reflection.html
danmux
Exemple génial Voici un
Sarath Sadasivan Pillai
Cela ne définit pas de champ struct. Cela définit un champ dans un pointeur sur struct. De toute évidence, cela ne répond pas à la question posée par OP.
wvxvw
14

Cela semble fonctionner:

package main

import (
    "fmt"
    "reflect"
)

type Foo struct {
    Number int
    Text string
}

func main() {
    foo := Foo{123, "Hello"}

    fmt.Println(int(reflect.ValueOf(foo).Field(0).Int()))

    reflect.ValueOf(&foo).Elem().Field(0).SetInt(321)

    fmt.Println(int(reflect.ValueOf(foo).Field(0).Int()))
}

Impressions:

123
321
Asgeir
la source
Merci! maintenant que j'ai lu les notes de peterSO, c'est parfaitement logique. J'utilisais foo, pas & foo, donc je ne pouvais pas être changé, et je ne savais pas ce qu'était Elem ().
cc young le