Usando riflettere, come si imposta il valore di un campo struct?


106

avere un brutto periodo di lavoro con i campi struct usando reflectpackage. in particolare, non ho capito come impostare il valore del campo.

digitare t struct {fi int; fs string}
var rt = t {123, "jblow"}
var i64 int64 = 456
  1. ottenere il nome del campo i - questo sembra funzionare

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

  2. ottenere il valore del campo i come a) interfaccia {}, b) int - questo sembra funzionare

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

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

  3. impostazione del valore del campo i - prova uno - panico

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

    panic : reflection.Value · SetInt utilizzando il valore ottenuto utilizzando un campo non esportato

    supponendo che non gli piacciano i nomi dei campi "id" e "name", quindi rinominati in "Id" e "Name"

    a) questa ipotesi è corretta?

    b) se corretto, ritenuto non necessario poiché nello stesso file / pacchetto

  4. impostazione del valore del campo i - provare due (con i nomi dei campi in maiuscolo) - panico

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

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

    panic : reflection.Value · SetInt utilizzando un valore non indirizzabile


Le istruzioni riportate di seguito da @peterSO sono complete e di alta qualità

Quattro. questo funziona:

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

documenta anche che i nomi dei campi devono essere esportabili (iniziano con la lettera maiuscola)


l'esempio più vicino che ho trovato per qualcuno che utilizzava reflectper impostare i dati era comments.gmane.org/gmane.comp.lang.go.general/35045 , ma anche lì faceva json.Unmarshalil lavoro sporco vero e proprio
cc young

(il commento sopra è obsoleto)
cc giovane

Risposte:


156

Go è disponibile come codice open source . Un buon modo per conoscere la riflessione è vedere come lo usano gli sviluppatori Go principali. Ad esempio, i pacchetti Go fmt e json . La documentazione del pacchetto contiene collegamenti ai file del codice sorgente sotto l'intestazione File del pacchetto.

Il pacchetto Go json esegue il marshalling e l'unmarshalling di JSON da e verso le strutture Go.


Ecco un esempio passo passo che imposta il valore di un structcampo evitando accuratamente gli errori.

Il reflectpacchetto Go ha una CanAddrfunzione.

func (v Value) CanAddr() bool

CanAddr restituisce true se l'indirizzo del valore può essere ottenuto con Addr. Tali valori sono chiamati indirizzabili. Un valore è indirizzabile se è un elemento di una fetta, un elemento di un array indirizzabile, un campo di una struttura indirizzabile o il risultato della dereferenziazione di un puntatore. Se CanAddr restituisce false, la chiamata di Addr andrà in panico.

Il reflectpacchetto Go ha una CanSetfunzione, che, se true, implica CanAddranche quella true.

func (v Value) CanSet() bool

CanSet restituisce true se il valore di v può essere modificato. Un valore può essere modificato solo se è indirizzabile e non è stato ottenuto mediante l'uso di campi struct non esportati. Se CanSet restituisce false, la chiamata a Set o qualsiasi setter specifico del tipo (ad esempio, SetBool, SetInt64) andrà in panico.

Dobbiamo assicurarci di poter giocare Setsul structcampo. Per esempio,

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

Se possiamo essere certi che tutti i controlli di errore non sono necessari, l'esempio si semplifica a,

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

5
Abbandonare! da qualche parte c'è una risposta, ma quattro ore di lavoro sul json pkg non me l'hanno ceduta. Per quanto riguarda il pacchetto di riflessione, estrarre le informazioni è piuttosto semplice, ma l'impostazione dei dati richiede un po 'di magia nera per la quale mi piacerebbe vedere un semplice esempio da qualche parte!
cc young

2
Eccezionale! se sei mai in Thailandia, per favore lascia che ti offra una birra o due o tre! grazie mille
cc giovane

5
Ottimo esempio pratico, questo articolo lo ha completamente demistificato per me golang.org/doc/articles/laws_of_reflection.html
danmux


Questo non imposta un campo struct. Questo imposta un campo in un puntatore a struct. Ovviamente, questo non risponde alla domanda posta da OP.
wvxvw

14

Questo sembra funzionare:

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

stampe:

123
321

Grazie! ora che ho letto gli appunti di peterSO, ha perfettamente senso. Stavo usando foo, not & foo, quindi non poteva essere cambiato, e non ero sicuro di cosa trattasse Elem ().
cc giovane
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.