英文:
Using reflect, how do you set the value of a struct field?
问题
getting a rough time working with struct fields using reflect
package. in particular, have not figured out how to set the field value.
<pre>
type t struct { fi int; fs string }
var r t = t{ 123, "jblow" }
var i64 int64 = 456
</pre>
- getting Name of field i - this seems to work
var field = reflect.TypeOf(r).Field(i).Name
- getting value of field i as a) interface{}, b) int - this seems to work
var iface interface{} = reflect.ValueOf(r).Field(i).Interface()
var i int = int(reflect.ValueOf(r).Field(i).Int())
- setting value of field i - try one - panic
reflect.ValueOf(r).Field(i).SetInt( i64 )
panic: reflect.Value·SetInt using value obtained using unexported field
assuming it did not like field names "id" and "name", so renamed to "Id" and "Name"
a) is this assumption correct?
b) if correct, thought not necessary since in same file / package
- setting value of field i - try two (with field names capitalized ) - panic
reflect.ValueOf(r).Field(i).SetInt( 465 )
reflect.ValueOf(r).Field(i).SetInt( i64 )
panic: reflect.Value·SetInt using unaddressable value
<hr />
Instructions below by @peterSO are thorough and high quality
Four. this works:
reflect.ValueOf(&r).Elem().Field(i).SetInt( i64 )
he documents as well that the field names must be exportable (begin with capital letter)
英文:
having a rough time working with struct fields using reflect
package. in particular, have not figured out how to set the field value.
<pre>
type t struct { fi int; fs string }
var r t = t{ 123, "jblow" }
var i64 int64 = 456
</pre>
- getting Name of field i - this seems to work
var field = reflect.TypeOf(r).Field(i).Name
- getting value of field i as a) interface{}, b) int - this seems to work
var iface interface{} = reflect.ValueOf(r).Field(i).Interface()
var i int = int(reflect.ValueOf(r).Field(i).Int())
- setting value of field i - try one - panic
reflect.ValueOf(r).Field(i).SetInt( i64 )
panic: reflect.Value·SetInt using value obtained using unexported field
assuming it did not like field names "id" and "name", so renamed to "Id" and "Name"
a) is this assumption correct?
b) if correct, thought not necessary since in same file / package
- setting value of field i - try two (with field names capitalized ) - panic
reflect.ValueOf(r).Field(i).SetInt( 465 )
reflect.ValueOf(r).Field(i).SetInt( i64 )
panic: reflect.Value·SetInt using unaddressable value
<hr />
Instructions below by @peterSO are thorough and high quality
Four. this works:
reflect.ValueOf(&r).Elem().Field(i).SetInt( i64 )
he documents as well that the field names must be exportable (begin with capital letter)
答案1
得分: 204
Go的json包可以将JSON编组和解组为Go结构。
下面是一个逐步示例,它在设置struct
字段的值时小心地避免错误。
func (v Value) CanAddr() bool
如果值的地址可以通过Addr获得,则CanAddr返回true。
这样的值被称为可寻址的。如果值是切片的元素、可寻址数组的元素、可寻址结构的字段或解引用指针的结果,则该值是可寻址的。如果CanAddr返回false,则调用Addr将引发panic。
Go的reflect
包有一个CanSet
函数,如果为true
,则意味着CanAddr
也为true
。
func (v Value) CanSet() bool
如果v的值可以更改,则CanSet返回true。只有当值是可寻址的并且不是通过使用未导出的结构字段获得的时,才能更改Value。如果CanSet返回false,则调用Set或任何类型特定的setter(例如SetBool、SetInt64)将引发panic。
我们需要确保我们可以Set
结构字段。例如,
package main
import (
"fmt"
"reflect"
)
func main() {
type t struct {
N int
}
var n = t{42}
// 开始时的N
fmt.Println(n.N)
// 结构体的指针-可寻址
ps := reflect.ValueOf(&n)
// 结构体
s := ps.Elem()
if s.Kind() == reflect.Struct {
// 导出的字段
f := s.FieldByName("N")
if f.IsValid() {
// 只有当值是可寻址的并且不是通过使用未导出的结构字段获得的时,才能更改Value。
if f.CanSet() {
// 更改N的值
if f.Kind() == reflect.Int {
x := int64(7)
if !f.OverflowInt(x) {
f.SetInt(x)
}
}
}
}
}
// 结束时的N
fmt.Println(n.N)
}
输出:
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包。包文档中有指向源代码文件的链接。
英文:
The Go json package marshals and unmarshals JSON from and to Go structures.
Here's a step-by-step example which sets the value of a struct
field while carefully avoiding errors.
The Go reflect
package has a CanAddr
function.
func (v Value) CanAddr() bool
> CanAddr returns true if the value's
> address can be obtained with Addr.
> Such values are called addressable. A
> value is addressable if it is an
> element of a slice, an element of an
> addressable array, a field of an
> addressable struct, or the result of
> dereferencing a pointer. If CanAddr
> returns false, calling Addr will
> panic.
The Go reflect
package has a CanSet
function, which, if true
, implies that CanAddr
is also true
.
func (v Value) CanSet() bool
> CanSet returns true if the value of v
> can be changed. A Value can be changed
> only if it is addressable and was not
> obtained by the use of unexported
> struct fields. If CanSet returns
> false, calling Set or any
> type-specific setter (e.g., SetBool,
> SetInt64) will panic.
We need to make sure we can Set
the struct
field. For example,
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
If we can be certain that all the error checks are unnecessary, the example simplifies to,
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)
}
BTW, Go is available as open source code. A good way to learn about reflection is to see how the core Go developers use it. For example, the Go fmt and json packages. The package documentation has links to the source code files under the heading Package files.
答案2
得分: 20
这似乎是有效的:
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
英文:
This seems to work:
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()))
}
Prints:
123
321
答案3
得分: 2
你可以创建一个辅助函数来设置值:
package main
import (
"fmt"
"log"
"reflect"
"time"
)
type Apple struct {
Color string
CreatedAt time.Time
}
func SetValue(obj any, field string, value any) {
ref := reflect.ValueOf(obj)
// 如果是指针,则解析其值
if ref.Kind() == reflect.Ptr {
ref = reflect.Indirect(ref)
}
if ref.Kind() == reflect.Interface {
ref = ref.Elem()
}
// 应该再次检查我们现在是否有一个结构体(仍然可能是任何类型)
if ref.Kind() != reflect.Struct {
log.Fatal("unexpected type")
}
prop := ref.FieldByName(field)
prop.Set(reflect.ValueOf(value))
}
func main() {
myStruct := Apple{}
SetValue(&myStruct, "Color", "red")
SetValue(&myStruct, "CreatedAt", time.Now())
fmt.Printf("Result: %+v\n", myStruct)
}
Playground: https://go.dev/play/p/uHoy5n3k1DW
英文:
You can create a helper function to set values:
package main
import (
"fmt"
"log"
"reflect"
"time"
)
type Apple struct {
Color string
CreatedAt time.Time
}
func SetValue(obj any, field string, value any) {
ref := reflect.ValueOf(obj)
// if its a pointer, resolve its value
if ref.Kind() == reflect.Ptr {
ref = reflect.Indirect(ref)
}
if ref.Kind() == reflect.Interface {
ref = ref.Elem()
}
// should double check we now have a struct (could still be anything)
if ref.Kind() != reflect.Struct {
log.Fatal("unexpected type")
}
prop := ref.FieldByName(field)
prop.Set(reflect.ValueOf(value))
}
func main() {
myStruct := Apple{}
SetValue(&myStruct, "Color", "red")
SetValue(&myStruct, "CreatedAt", time.Now())
fmt.Printf("Result: %+v\n", myStruct)
}
Playground: https://go.dev/play/p/uHoy5n3k1DW
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论