如何通过反射来设置结构字段的值?

在使用 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 )

    Panic : response. Value · SetInt 使用使用未导出字段获得的值

    假设它不喜欢字段名“ Id”和“ Name”,因此将其重命名为“ Id”和“ Name”

    A)这个假设正确吗?

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

  4. 设置字段 i 的值-尝试二(字段名称大写)-慌张

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

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

    使用无法寻址的值进行设置


下面由@peterSO 提供的说明是全面而高质量的

四,这个可以:

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

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

123799 次浏览

Go JSON 包封送和解封从和到 Go 的 JSON 结构。

下面是一个逐步的示例,它设置 struct字段的值,同时小心地避免错误。

Go reflect包有一个 CanAddr函数。

func (v Value) CanAddr() bool

如果值为 地址可以通过 Addr 获得。 这样的值称为可寻址的 值是可寻址的,如果它是 切片的元素,切片的元素 可寻址数组中的一个字段 可寻址的结构,或结果 取消对指针的引用 返回 false,调用 Addr will 恐慌。

Go reflect包有一个 CanSet函数,如果是 true,则意味着 CanAddr也是 true

func (v Value) CanSet() bool

如果 v 的值为 值可以更改 只有当它是可寻址的,而不是 通过使用未出口的 如果 CanSet 返回 False,调用 Set 或任意 类型特定的 setter (例如,SetBool, SetInt64)会恐慌。

我们需要确保我们可以 Setstruct字段。例如,

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

顺便说一句,Go 是 开放源代码开放源代码。了解反射的一个好方法是看看核心 Go 开发人员如何使用它。例如,Go FmtJson包。包文档有指向标题 Package 文件下的源代码文件的链接。

这似乎行得通:

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