回答

收藏

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

技术问答 技术问答 286 人阅读 | 0 人回复 | 2023-09-12

使用reflect包处理结构字段时遇到困难。特别是,我还没有考虑如何设置字段值。$ v/ \8 I1 r* v0 [  j! c/ J1 ~; Z- d
输入 t struct { fi int; fs var rt = t{ 123,"jblow" }var i64 int64 = 456[ol]获取字段 i 的名称 - 这似乎有效[/ol]var field = reflect.TypeOf(r).Field(i).Name9 V5 u; V* T/ F; D1 A3 Y7 Q' A/ H
[ol]将字段 i 的值作为 a) interface{},b) int - 这似乎有效[/ol]var iface interface{} = reflect.ValueOf(r).Field(i).Interface()( \% w; w  w* j% p& Z
var i int = int(reflect.ValueOf(r).Field(i).Int())
: R; {, j0 F/ v[ol]设置字段 i 的值 -尝试一个 - 恐慌[/ol]reflect.ValueOf(r).Field(i).SetInt( i64 )6 s: I8 x( J5 y2 `
恐慌:reflect.Value·SetInt 使用未导出字段获得的值
& t7 c9 `6 n3 d( S3 s假设它不喜欢字段名假设它不喜欢字段名id”和“name所以重命名为Id”和“Name”5 w; Z1 P0 l2 x. f1 s7 @, z
a) 这个假设正确吗?
6 `+ _/ I6 O- X: x3 ub) 如果是正确的,认为没有必要,因为在同一个文件/包中% T) {9 u6 C0 @5 i9 r
[ol]设置字段 i 的值 - 尝试两个(字段名称大写) - 恐慌[/ol]reflect.ValueOf(r).Field(i).SetInt( 465 )
2 h% v8 Q4 z9 d" y2 m/ o7 W+ vreflect.ValueOf(r).Field(i).SetInt( i64 )
2 _" x# ~/ q4 a! N* l6 s* Z1 P恐慌:reflect.Value·SetInt 使用不可寻址值  b9 w% }. m. n. K! B1 d
@peterSO  以下描述全面、高质量
. Y" W# c; f3 o  Z6 p4 q四、有效:
6 M! ?1 p, y. ?1 W" N% J5 j% e5 S) freflect.ValueOf(&r).Elem().Field(i).SetInt( i64 )他还记录了字段名必须是可导出的(从大写字母开始)
4 _& j: v8 V! S                                                               
" V+ G& Q8 R4 ]3 j# G. v    解决方案:                                                                - C/ `/ {' i0 m$ Y. Q) m  c7 y
                                                                Go json 包在 Go 结构中 JSON 编组和解组。8 h8 y& {# y* i1 J4 z* S' m
这是一个分步示例struct设置字段值时要小心避免错误。+ v& @' i! E5 J
Goreflect包有一个CanAddr功能。
* _; o+ V  t) Y
    func (v Value) CanAddr() bool
    1 i9 q* X" _8 o+ X: F. ^6 C
如果可以使用 Addr 获取值的地址, CanAddr 返回 true。这样的值称为可寻址的。如果值是切片的元素、可寻址数组的元素、可寻址结构的字段或取消引用指针的结果,则该值是可寻址的。如果 CanAddr 返回 false,则调用 Addr 会导致恐慌。
( h* B# C( g' B) s$ qGoreflect包有一个CanSet函数,如果true,则意味着它CanAddr也是true。% J8 y/ l, u4 _6 m, K; T1 t
    func (v Value) CanSet() bool
    7 V' m9 [4 r- U2 b( X
如果可以改变 v , CanSet 返回 true。只有当值可以找到,而不是通过使用未导出的结构字段获得时,才能更改值。CanSet 返回 false,则调用 Set 或任何特定类型的 setter(例如 SetBool、SetInt64)恐慌会发生。
# |+ Z; F# V, K% ~7 N/ [我们需要确保我们能够Set在struct现场。例如,,
" x/ Q/ _0 }8 `, e
    package mainimport  "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:427
      @5 y2 [  m/ |# s- O) d
假如我们能确定所有的错误检查都是不必要的,那么这个例子就简化为,
  `3 W1 @7 S% C: q8 d
    package mainimport  "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)}
    " d  d! i. a, V/ N2 \
顺便说一句,Go 可用作开源代码。了解反射的好方法之一是了解核心 Go 开发人员如何使用它。例如,Go fmt和json包。包文件在包文件标题下提供指向源代码文件的链接。
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则