使用reflect,如何设置struct字段的值?

时间:2011-06-18 09:24:05

标签: go reflection go-reflect

使用reflect包使用struct字段时非常困难。特别是,还没弄清楚如何设置字段值。

type t struct { fi int; fs string }
var r t = t{ 123, "jblow" }
var i64 int64 = 456
  1. 获取字段名称i - 这似乎有效

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

  2. 将字段i的值作为a)接口{},b)int - 这似乎有用

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

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

  3. 设置字段i的值 - 尝试一个 - 恐慌

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

    恐慌:reflect.Value·使用未导出字段获取的值的SetInt

    假设它不喜欢字段名称“id”和“name”,所以重命名为“Id”和“Name”

    a)这个假设是正确的吗?

    b)如果正确,认为没有必要,因为在同一个文件/包中

  4. 设置字段i的值 - 尝试两个(字段名称大写) - 恐慌

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

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

    恐慌:reflect.Value·SetInt使用不可追踪的值


  5. @peterSO下面的说明是彻底和高质量的

    四个。这有效:

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

    他还记录了字段名称必须是可导出的(以大写字母开头)

2 个答案:

答案 0 :(得分:125)

Go以open source code的形式提供。了解反射的一个好方法是看看核心Go开发人员如何使用它。例如,Go fmtjson包。包文档包含指向包文件下的源代码文件的链接。

Go json包编组并从Go结构中解组JSON。


这是一个循序渐进的示例,它设置struct字段的值,同时小心避免错误。

Go reflect包具有CanAddr功能。

func (v Value) CanAddr() bool
  

如果值为,则CanAddr返回true   地址可以用Addr获得。   这些值称为可寻址。一个   如果是值,则值是可寻址的   切片的元素,元素的元素   可寻址数组,一个字段   可寻址结构,或结果   取消引用指针。如果CanAddr   返回false,调用Addr会   恐慌。

Go reflect包具有CanSet功能,如果true,则表示CanAddr也是true

func (v Value) CanSet() bool
  

如果v的值,CanSet返回true   可以改变。值可以更改   只有它是可寻址的而不是   通过使用未经报告获得   结构字段。如果CanSet返回   false,调用Set或any   特定于类型的setter(例如,SetBool,   SetInt64)会恐慌。

我们需要确保Set字段为struct。例如,

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

如果我们可以确定所有错误检查都是不必要的,那么示例将简化为

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

答案 1 :(得分:11)

这似乎有效:

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

打印:

123
321