使用反射,如何设置结构体字段的值?

huangapple go评论121阅读模式
英文:

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>

  1. getting Name of field i - this seems to work

var field = reflect.TypeOf(r).Field(i).Name

  1. 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())

  1. 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

  1. 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(&amp;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>

  1. getting Name of field i - this seems to work

var field = reflect.TypeOf(r).Field(i).Name

  1. 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())

  1. 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

  1. 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(&amp;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字段的值时小心地避免错误。

Go的reflect包有一个CanAddr函数。

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的fmtjson包。包文档中有指向源代码文件的链接。

英文:

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 (
	&quot;fmt&quot;
	&quot;reflect&quot;
)

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(&amp;n)
	// struct
	s := ps.Elem()
	if s.Kind() == reflect.Struct {
		// exported field
		f := s.FieldByName(&quot;N&quot;)
		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 (
	&quot;fmt&quot;
	&quot;reflect&quot;
)

func main() {
	type t struct {
		N int
	}
	var n = t{42}
	fmt.Println(n.N)
	reflect.ValueOf(&amp;n).Elem().FieldByName(&quot;N&quot;).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 (
	&quot;fmt&quot;
	&quot;reflect&quot;
)

type Foo struct {
	Number int
	Text string
}

func main() {
	foo := Foo{123, &quot;Hello&quot;}

	fmt.Println(int(reflect.ValueOf(foo).Field(0).Int()))

	reflect.ValueOf(&amp;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 (
	&quot;fmt&quot;
	&quot;log&quot;
	&quot;reflect&quot;
	&quot;time&quot;
)

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(&quot;unexpected type&quot;)
	}

	prop := ref.FieldByName(field)
	prop.Set(reflect.ValueOf(value))
}

func main() {
	myStruct := Apple{}
	SetValue(&amp;myStruct, &quot;Color&quot;, &quot;red&quot;)
	SetValue(&amp;myStruct, &quot;CreatedAt&quot;, time.Now())
	fmt.Printf(&quot;Result: %+v\n&quot;, myStruct)
}

Playground: https://go.dev/play/p/uHoy5n3k1DW

huangapple
  • 本文由 发表于 2011年6月18日 17:24:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/6395076.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定