回答

收藏

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

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

使用reflect包处理结构字段时遇到困难。特别是,我还没有考虑如何设置字段值。  F% F7 ~6 }: |8 v
输入 t struct { fi int; fs var rt = t{ 123,"jblow" }var i64 int64 = 456[ol]获取字段 i 的名称 - 这似乎有效[/ol]var field = reflect.TypeOf(r).Field(i).Name( n. }1 h, P2 V- c
[ol]将字段 i 的值作为 a) interface{},b) int - 这似乎有效[/ol]var iface interface{} = reflect.ValueOf(r).Field(i).Interface()- I$ ^1 D7 p& [* }
var i int = int(reflect.ValueOf(r).Field(i).Int())% E: J! O  Z% ?& H  t8 N9 W
[ol]设置字段 i 的值 -尝试一个 - 恐慌[/ol]reflect.ValueOf(r).Field(i).SetInt( i64 )
5 t9 k0 c/ G! n1 ~) X1 J2 O恐慌:reflect.Value·SetInt 使用未导出字段获得的值
' h7 v3 f4 k% t1 n% g假设它不喜欢字段名假设它不喜欢字段名id”和“name所以重命名为Id”和“Name”$ Y7 F0 O, n5 Z, I! E
a) 这个假设正确吗?
) l. y+ u/ S! B" B6 E3 T+ \7 Rb) 如果是正确的,认为没有必要,因为在同一个文件/包中7 h& C& |+ s. c  F7 o5 N: n  c6 j  E
[ol]设置字段 i 的值 - 尝试两个(字段名称大写) - 恐慌[/ol]reflect.ValueOf(r).Field(i).SetInt( 465 )
: M: q$ P) a+ j' a7 L+ g- xreflect.ValueOf(r).Field(i).SetInt( i64 )& l( {  m. w8 T1 f6 B& D
恐慌:reflect.Value·SetInt 使用不可寻址值: ]* f+ `4 T- k6 A
@peterSO  以下描述全面、高质量
# p- I9 q9 _7 T, ]2 w3 X9 T: I, m四、有效:
- V5 R! n9 h' B/ N" P1 o& H' Kreflect.ValueOf(&r).Elem().Field(i).SetInt( i64 )他还记录了字段名必须是可导出的(从大写字母开始)  Y, h, C" w8 Y' _- F! O
                                                               
( H( ^5 H, X# O  r$ s5 Q    解决方案:                                                               
# e/ [. ~* e6 H' Y9 w2 |) q                                                                Go json 包在 Go 结构中 JSON 编组和解组。7 R( x$ [2 s( _! Z1 _
这是一个分步示例struct设置字段值时要小心避免错误。
9 ?) t9 t& {, ^( C( MGoreflect包有一个CanAddr功能。8 b' n% L: E8 s! H% @; a/ P) g' m
    func (v Value) CanAddr() bool
      \: z/ U  k, q8 c; _
如果可以使用 Addr 获取值的地址, CanAddr 返回 true。这样的值称为可寻址的。如果值是切片的元素、可寻址数组的元素、可寻址结构的字段或取消引用指针的结果,则该值是可寻址的。如果 CanAddr 返回 false,则调用 Addr 会导致恐慌。
* k$ Y) i7 w0 s! q/ K8 iGoreflect包有一个CanSet函数,如果true,则意味着它CanAddr也是true。
9 D3 ?* l( x' e9 ^1 K9 V( ^
    func (v Value) CanSet() bool# _4 ]3 M  ]! B3 o) C/ V2 [
如果可以改变 v , CanSet 返回 true。只有当值可以找到,而不是通过使用未导出的结构字段获得时,才能更改值。CanSet 返回 false,则调用 Set 或任何特定类型的 setter(例如 SetBool、SetInt64)恐慌会发生。
& h- j" W+ B1 g, X% Q- }+ m我们需要确保我们能够Set在struct现场。例如,,
/ p* y# |4 h7 x  S9 B
    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
    9 o2 c4 ]6 [& N! A
假如我们能确定所有的错误检查都是不必要的,那么这个例子就简化为,) ^' Y) n4 N7 E2 o2 ^- o' G1 j
    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)}. t0 |8 E8 i" s
顺便说一句,Go 可用作开源代码。了解反射的好方法之一是了解核心 Go 开发人员如何使用它。例如,Go fmt和json包。包文件在包文件标题下提供指向源代码文件的链接。
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则