回答

收藏

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

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

使用reflect包处理结构字段时遇到困难。特别是,我还没有考虑如何设置字段值。1 V7 o9 m8 l4 Y+ U' ^
输入 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
! H& x! [/ a3 Y# D[ol]将字段 i 的值作为 a) interface{},b) int - 这似乎有效[/ol]var iface interface{} = reflect.ValueOf(r).Field(i).Interface()5 o' m' C1 e* ]4 T& ?
var i int = int(reflect.ValueOf(r).Field(i).Int())+ q! p. u4 T2 i+ k+ p
[ol]设置字段 i 的值 -尝试一个 - 恐慌[/ol]reflect.ValueOf(r).Field(i).SetInt( i64 )4 _$ T, Q; G" `% h* R. v+ h6 Y
恐慌:reflect.Value·SetInt 使用未导出字段获得的值6 O/ e8 u' p/ `4 @: I
假设它不喜欢字段名假设它不喜欢字段名id”和“name所以重命名为Id”和“Name”
. F' d4 s( @* {% h7 x. W- Ta) 这个假设正确吗?' n1 I! P( [4 r) r7 K: k  q
b) 如果是正确的,认为没有必要,因为在同一个文件/包中
4 u3 X" F7 h: u[ol]设置字段 i 的值 - 尝试两个(字段名称大写) - 恐慌[/ol]reflect.ValueOf(r).Field(i).SetInt( 465 )& M" g$ x: s: F& Q
reflect.ValueOf(r).Field(i).SetInt( i64 )+ q5 h4 ^: H  R, u
恐慌:reflect.Value·SetInt 使用不可寻址值
, D# W( u* o, J7 P' j@peterSO  以下描述全面、高质量% t. Y) k+ z# M
四、有效:
& Q9 g7 |! ~  e" a: r8 creflect.ValueOf(&r).Elem().Field(i).SetInt( i64 )他还记录了字段名必须是可导出的(从大写字母开始): o, {% |" J0 q" |
                                                                - j* m: C; b  U, N& Q8 K; y" M
    解决方案:                                                                ! d8 v5 e, c+ V
                                                                Go json 包在 Go 结构中 JSON 编组和解组。
2 M- W+ d& B1 f1 O$ i3 }" I) o" C5 {这是一个分步示例struct设置字段值时要小心避免错误。
, m  ^! o+ }) f, ]/ ]2 }  k" z1 BGoreflect包有一个CanAddr功能。& l6 @* y0 n0 [. ?0 Q& T
    func (v Value) CanAddr() bool; y* R* n6 G1 r5 V
如果可以使用 Addr 获取值的地址, CanAddr 返回 true。这样的值称为可寻址的。如果值是切片的元素、可寻址数组的元素、可寻址结构的字段或取消引用指针的结果,则该值是可寻址的。如果 CanAddr 返回 false,则调用 Addr 会导致恐慌。2 t/ g. V- q6 ~: z/ }
Goreflect包有一个CanSet函数,如果true,则意味着它CanAddr也是true。
% k; d! C) K1 S  b9 R0 z8 z( [
    func (v Value) CanSet() bool
    % O5 K) R+ J0 p; T) C9 p! v
如果可以改变 v , CanSet 返回 true。只有当值可以找到,而不是通过使用未导出的结构字段获得时,才能更改值。CanSet 返回 false,则调用 Set 或任何特定类型的 setter(例如 SetBool、SetInt64)恐慌会发生。
: X. c. a( d% C. b# ?: c我们需要确保我们能够Set在struct现场。例如,," k) ]( Q. M, p
    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
    - }: e  F: _) F
假如我们能确定所有的错误检查都是不必要的,那么这个例子就简化为,
" A( Y" ~0 d# S8 U! ~5 i& f5 Q
    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)}# v5 @" G5 u" ?" ~( E
顺便说一句,Go 可用作开源代码。了解反射的好方法之一是了解核心 Go 开发人员如何使用它。例如,Go fmt和json包。包文件在包文件标题下提供指向源代码文件的链接。
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则