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

发布于 2024-11-16 07:29:34 字数 1117 浏览 3 评论 0原文

使用 reflect 包处理结构字段时遇到了困难。特别是还没弄清楚如何设置字段值。

type t struct { fi int; fs string }
var r t = t{ 123, "jblow" }
var i64 int64 = 456
  1. 获取字段 i 的名称 - 这似乎有效

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

  2. 获取字段 i 的值作为 a) interface{}, b) int - 这似乎有效

    var iface 接口{} =reflect.ValueOf(r).Field(i).Interface()

    var i int = int(reflect.ValueOf(r).Field(i).Int())

  3. 设置字段 i 的值 - 尝试一个 - 恐慌

    reflect.ValueOf(r).Field(i).SetInt( i64 )

    恐慌:reflect.Value·SetInt使用通过未导出字段获得的值

    假设它不喜欢字段名称“id”和“name”,因此重命名为“Id”和“Name”

    a)这个假设正确吗?

    b) 如果正确,认为没有必要,因为在同一个文件/包中

  4. 设置字段 i 的值 - 尝试两个(字段名称大写) - 恐慌

    reflect.ValueOf(r).Field(i).SetInt(465)

    reflect.ValueOf(r).Field(i).SetInt( i64 )

    恐慌:reflect.Value·SetInt使用不可寻址的值


@peterSO 下面的说明是彻底且高质量的

四。这有效:

reflect.ValueOf(&r).Elem().Field(i).SetInt( i64 )

他还记录了字段名称必须可导出(以大写字母开头)

having a rough time working with struct fields using reflect package. in particular, have not figured out how to set the field value.

type t struct { fi int; fs string }
var r t = t{ 123, "jblow" }
var i64 int64 = 456
  1. getting Name of field i - this seems to work

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

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

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

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


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)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

暖阳 2024-11-23 07:29:34

Go json 包从 Go 结构编组和解组 JSON。

下面是一个分步示例,它设置 struct 字段的值,同时小心避免错误。

Go reflect 包有一个 CanAddr 函数。

func (v Value) CanAddr() bool

如果值是,则 CanAddr 返回 true
地址可以通过Addr获得。
这样的值称为可寻址的。一个
值是可寻址的,如果它是
切片的元素、a 的元素
可寻址数组,一个字段
可寻址结构,或结果
取消引用指针。如果可以添加
返回 false,调用 Addr 将
恐慌。

Go reflect 包有一个 CanSet 函数,如果 true,则意味着 CanAddr 也是

func (v Value) CanSet() bool

如果 v 的值是 CanSet 返回 true
可以改变。 A 值可以改变
仅当它是可寻址且不可寻址时
通过使用未出口获得
结构体字段。如果 CanSet 返回
false,调用 Set 或任何
特定于类型的 setter(例如,SetBool、
SetInt64) 会出现恐慌。

我们需要确保我们可以设置 struct 字段。例如,

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 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 (
    "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.

離殇 2024-11-23 07:29:34

这似乎有效:

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
天涯沦落人 2024-11-23 07:29:34

您可以创建一个辅助函数来设置值:

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

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文