2014-04-28 9 views
8

Ho una persona struct.Come si fa a scorrere i campi in una struttura Golang per ottenere e impostare i valori in modo estensibile?

type Person struct { 
    Firstname string  
    Lastname string  
    Years  uint8  
} 

allora ho due istanze di questo struct, Persona e PersonB.

PersonA := {"", "Obama", 6} 
PersonB := {"President", "Carter", 8} 

voglio scrivere una funzione che copia i valori da Persona a PersonB data una certa condizione per ogni campo (cioè non vuoto). So come farlo codificando i nomi dei campi, ma voglio una funzione che funzioni anche se cambio la struttura Person.

So che i riflessi di Go sono utili, ma il problema si sta verificando e l'impostazione dei valori richiede la conoscenza dei tipi, se si desidera utilizzare qualcosa come SetInt. Ma c'è un modo "semplice" per farlo?

** analogia Javascript ** In Javascript, è sufficiente eseguire un ciclo (per la proprietà in someObject).

(for propt in personA) { 
    if personA[propt] != "" { 
    // do something 
    personB[propt] = personA[propt] 
    } 
} 

Opzioni ho escluso:

  1. Tenere traccia dei campi in ogni struttura in una mappa, quindi utilizzando una combinazione di FieldByName e la raccolta di Set * funzioni nel reflect pkg.

  2. Creazione di un ciclo attraverso i campi di Persona manualmente (sotto). Perché voglio fare questo tipo di "aggiornamento" per molti altri le strutture (scuola, animali, ecc ...)

    if PersonA.Firstname != "" { 
        PersonB.Firstname = PersonA.Firstname 
    } 
    

    ...

    if PersonA.Years != "" { 
        PersonB.Years = PersonA.Years 
    } 
    

La domanda qui sotto mi viene a metà strada, ma non è ancora estensibile a tutte le strutture per le quali voglio utilizzare questa funzione di "aggiornamento".

in golang, using reflect, how do you set the value of a struct field?

** Altri collegamenti utili ** GoLang: Access struct property by name

risposta

2

riflessione dovrebbe essere tutto il necessario. Questo sembra simile (anche se non identico) alla semantica "deep copy", che è stata implementata a https://godoc.org/code.google.com/p/rog-go/exp/deepcopy

Dovresti essere in grado di adattarlo alle tue esigenze, o almeno di prendere qualche idea da esso.

+0

C'è un costo enorme, in termini di prestazioni e la complessità del codice, per utilizzando la riflessione. @platwp, potrebbe valere la pena di postare una domanda di livello superiore su ciò che stai cercando di costruire quando verrà fuori. Se la flessibilità di JavaScript-ish è così importante, potresti anche voler utilizzare le raccolte dinamiche ('map [string] interface {}' o qualsiasi altra cosa) invece dei tipi di oggetto. – twotwotwo

+0

@twotwotwo Speravo di evitare la parte "complessità". Se non esiste un modo "facile" per farlo, allora le raccolte dinamiche potrebbero avere senso. Grazie! – platwp

1

Dovresti usare uno map[string]interface{} invece, sarà molto più veloce (anche se non è ancora veloce come hai usato la logica corretta con le strutture reali).

package main 

import "fmt" 

type Object map[string]interface{} 

var m = Object{ 
    "Firstname": "name", 
    "Lastname": "", 
    "years":  uint8(10), 
} 

func main() { 
    var cp = Object{} 
    for k, v := range m { 
     if s, ok := v.(string); ok && s != "" { 
      cp[k] = s 
     } else if ui, ok := v.(uint8); ok { 
      cp[k] = ui 
     } 
    } 
    fmt.Printf("%#v\n", cp) 
} 
3

Ecco la soluzione f2.Set(reflect.Value(f)) è la chiave qui

package main 

    import (
    "fmt" 
    "reflect" 
    ) 

    func main() { 
    type T struct { 
     A int 
     B string 
    } 
    t := T{23, "skidoo"} 
    t2:= T{} 
    s := reflect.ValueOf(&t).Elem() 
    s2 := reflect.ValueOf(&t2).Elem() 
    typeOfT := s.Type() 
    fmt.Println("t=",t) 
    fmt.Println("t2=",t2) 

    for i := 0; i < s.NumField(); i++ { 
     f := s.Field(i) 
     f2:= s2.Field(i) 
     fmt.Printf("%d: %s %s = %v\n", i, 
      typeOfT.Field(i).Name, f.Type(), f.Interface()) 
     fmt.Printf("%d: %s %s = %v\n", i, 
      typeOfT.Field(i).Name, f2.Type(), f2.Interface()) 
     f2.Set(reflect.Value(f)) 
     fmt.Printf("%d: %s %s = %v\n", i, 
      typeOfT.Field(i).Name, f2.Type(), f2.Interface()) 

    } 
    fmt.Println("t=",t) 
    fmt.Println("t2=",t2) 
} 

Output: 

t= {23 skidoo} 
t2= {0 } 
0: A int = 23 
0: A int = 0 
0: A int = 23 
1: B string = skidoo 
1: B string = 
1: B string = skidoo 
t= {23 skidoo} 
t2= {23 skidoo} 

http://play.golang.org/p/UKFMBxfbZD

0

Utilizzare la riflettono.ValueOf(), ha l'interfaccia per convertire in tipo concreto e ha SetXXX API per impostare il valore desiderato.

fields := reflect.TypeOf(structValue) 
values := reflect.ValueOf(structValue) 

num := fields.NumField() 

for i := 0; i < num; i++ { 
    field := fields.Field(i) 
    value := values.Field(i) 
    fmt.Print("Type:", field.Type, ",", field.Name, "=", value, "\n") 

    switch value.Kind() { 
    case reflect.String: 
     v := value.String()) 
     fmt.Print(v, "\n") 
    case reflect.Int: 
     v := strconv.FormatInt(value.Int(), 10) 
     fmt.Print(v, "\n") 
    case reflect.Int32: 
     v := strconv.FormatInt(value.Int(), 10) 
     fmt.Print(v, "\n") 
    case reflect.Int64: 
     v := strconv.FormatInt(value.Int(), 10) 
     fmt.Print(v, "\n") 
    default: 
     assert.Fail(t, "Not support type of struct") 
    } 
} 
Problemi correlati