124

使用 package.json 处理结构字段时遇到了困难reflect。特别是,还没弄清楚如何设置字段值。

类型 t 结构 { fi int; fs 字符串 }
var rt = t{ 123, “jblow” }
var i64 int64 = 456
  1. 获取字段 i 的名称 - 这似乎有效

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

  2. 将字段 i 的值作为 a) interface{}, 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 使用不可寻址的值


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

四。这有效:

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

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

4

2 回答 2

181

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

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

Goreflect包有一个CanAddr功能。

func (v Value) CanAddr() bool

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

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

func (v Value) CanSet() bool

如果 v 的值可以更改,CanSet 返回 true。只有当它是可寻址的并且不是通过使用未导出的结构字段获得时,才可以更改值。如果 CanSet 返回 false,则调用 Set 或任何特定类型的设置器(例如,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包。包文档在标题包文件下有指向源代码文件的链接。

于 2011-06-18T14:29:22.170 回答
16

这似乎有效:

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
于 2011-06-19T13:27:32.690 回答