2011-06-18 10 views
63

teniendo problemas para trabajar con campos struct usando el paquete reflect. en particular, no han descubierto cómo establecer el valor del campo.Usando reflect, ¿cómo establece el valor de un campo struct?

 
type t struct { fi int; fs string } 
var r t = t{ 123, "jblow" } 
var i64 int64 = 456 
  1. obtener el nombre del campo i - esto parece funcionar

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

  2. obtener el valor de campo i como: a) la interfaz {}, b) int - esto parece funcionar

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

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

  3. valor de ajuste del campo i - probar uno - pánico

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

    pánico: reflect.Value · setInt utilizando el valor obtenido utilizando el campo dejados de

    suponiendo que no le gustaba el campo nombres "id" y "nombre", por lo que renombraron a "Id" y "Nombre"

    a) ¿Es correcta esta suposición?

    b) si es correcto, no se estimó necesario ya que en mismo archivo/paquete

  4. valor de ajuste del campo i - Intento de dos (con nombres de campo en mayúscula) - pánico

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

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

    pánico: reflejar.Valor · SetInt usando el valor no direccionable


instrucciones de abajo por @peterSO son completa y alta calidad

Four. esto funciona:

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

documenta también que los nombres de campo deben ser exportables (comienzan con letra mayúscula)

+0

el ejemplo más cercano que pude encontrar para alguien que usa 'reflect' para establecer datos era http://comments.gmane.org/gmane.comp.lang.go.general/35045, pero incluso allí usó' json.Unmarshal' para hacer el trabajo sucio real –

+0

(el el comentario anterior está obsoleto) –

Respuesta

93

Go está disponible como open source code. Una buena forma de aprender sobre la reflexión es ver cómo la utilizan los desarrolladores centrales de Go. Por ejemplo, los paquetes Go fmt y json. La documentación del paquete tiene enlaces a los archivos de código fuente bajo el encabezado Archivos de paquete.

El paquete Go json reúne y quita JSON de y para las estructuras Go.


Aquí está un ejemplo paso a paso que establece el valor de un campo struct, evitando cuidadosamente los errores. El paquete Go reflect tiene una función CanAddr.

func (v Value) CanAddr() bool 

CanAddr devuelve verdadero si la dirección del valor se puede obtener con Dir. Dichos valores se llaman direccionables. Un valor es direccionable si es un elemento de un sector, un elemento de un conjunto direccionable , un campo de una estructura direccionable , o el resultado de desreferenciando un puntero. Si CanAddr devuelve falso, llamar a Addr hará que entre en pánico.

El reflect paquete Go tiene una función CanSet, que, si true, implica que CanAddr es también true.

func (v Value) CanSet() bool 

CanSet devuelve verdadero si el valor de v se puede cambiar. Un valor se puede cambiar solo si es direccionable y no fue obtenido mediante el uso de campos de estructura no exportados. Si CanSet devuelve falso, llamar a Set o cualquier setter específico de tipo (por ejemplo, SetBool, SetInt64) entrará en pánico.

Tenemos que asegurarnos de que podamos Set el campo struct. Por ejemplo,

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 podemos estar seguros de que todas las comprobaciones de errores son innecesarias, el ejemplo se simplifica 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) 
} 
+0

yendo allí ahora –

+2

Give Up! en algún lugar hay una respuesta, pero cuatro horas de trabajo en el json pkg no me lo han dado. El paquete refleja, la extracción de información es bastante sencilla, pero establecer datos requiere algo de magia negra por lo que me gustaría ver un ejemplo simple en alguna parte. –

+2

¡Excepcional! si alguna vez vas a Tailandia, ¡por favor déjame invitarte a una cerveza o dos o tres! muchas gracias –

6

Esto parece funcionar:

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

Prints:

123 
321 
+0

gracias! ahora que he leído las notas de peterSO, esto tiene mucho sentido. Estaba usando foo, not & foo, por lo que no pude cambiar, y no estaba seguro de qué era Elem(). –

Cuestiones relacionadas