使用reflect包处理结构字段时遇到了困难。特别是,还没想好如何设置字段值。
reflect
输入 t struct { fi int; fs 字符串 } var rt = t{ 123, "jblow" } var i64 int64 = 456
var field = reflect.TypeOf(r).Field(i).Name
var iface interface{} = reflect.ValueOf(r).Field(i).Interface()
var i int = int(reflect.ValueOf(r).Field(i).Int())
reflect.ValueOf(r).Field(i).SetInt( i64 )
恐慌:reflect.Value·SetInt 使用使用未导出字段获得的值
假设它不喜欢字段名称“id”和“name”,所以重命名为“Id”和“Name”
a) 这个假设正确吗?
b) 如果正确,认为没有必要,因为在同一个文件/包中
reflect.ValueOf(r).Field(i).SetInt( 465 )
恐慌:reflect.Value·SetInt 使用不可寻址的值
@peterSO 下面的说明是全面和高质量的
四。这有效:
reflect.ValueOf(&r).Elem().Field(i).SetInt( i64 )
他还记录了字段名称必须是可导出的(以大写字母开头)
Go json 包在 Go 结构中对 JSON 进行编组和解组。
这是一个分步示例,它struct在小心避免错误的同时设置字段的值。
struct
Goreflect包有一个CanAddr功能。
CanAddr
func (v Value) CanAddr() bool
如果可以使用 Addr 获取值的地址,则 CanAddr 返回 true。这样的值称为可寻址的。如果值是切片的元素、可寻址数组的元素、可寻址结构的字段或取消引用指针的结果,则该值是可寻址的。如果 CanAddr 返回 false,则调用 Addr 将导致恐慌。
Goreflect包有一个CanSet函数,如果true,则意味着它CanAddr也是true。
CanSet
true
func (v Value) CanSet() bool
如果可以更改 v 的值,则 CanSet 返回 true。只有当值是可寻址的并且不是通过使用未导出的结构字段获得时,才可以更改值。如果 CanSet 返回 false,则调用 Set 或任何特定于类型的 setter(例如 SetBool、SetInt64)将发生恐慌。
我们需要确保我们可以Set在struct现场。例如,
Set
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 fmt和json包。包文档在包文件标题下提供了指向源代码文件的链接。