gpt4 book ai didi

go - 如何使用反射更改结构片段的字段?

转载 作者:IT王子 更新时间:2023-10-29 01:57:32 26 4
gpt4 key购买 nike

我有以下 https://play.golang.org/p/TlHCX29QZr

package main

import (
"fmt"
"reflect"
)

type A struct {
Name string
Age int
}

func change(a interface{}) {
aa := reflect.Indirect(reflect.ValueOf(a))

for i := 0; i < aa.NumField(); i++ {
field := aa.Field(i)

switch field.Interface().(type) {
case string:
field.Set(reflect.ValueOf("fred"))
case int:
field.Set(reflect.ValueOf(54))
default:
fmt.Println("unknown field")
}
}
}

func main() {
a := &A{"bob", 120}
b := []*A{}
c := []struct {
Alias string
Months int
}{}

d := []struct {
First string
Years int
}{
{"james", 22},
{"ricky", 32},
{"bobby", 12},
{"rachel", 82},
}


change(a)
fmt.Println(a) // want &A{"fred", 54}

change(b)
fmt.Println(b) // want []*A{&A{"fred", 54}}

change(c)
fmt.Println(c) // want []struct{struct{"fred", 54}}

change(d)
fmt.Println(d) // want []struct{struct{"fred", 54}, struct{"fred", 54}, struct{"fred", 54}, struct{"fred", 54}}
}

如您所见,有些变量是空 slice ,有些则不是。对于那些空的,我需要添加 1 个结构 {"fred", 54}。对于那些不为空的 slice ,我需要将所有值更改为 {"fred", 54}。我事先不知道这些字段是什么...只是如果有一个字符串字段,该值应该是“fred”,如果是一个 int 字段,则该值应该是 54。

我能够更改“a”的值,但其他一切都失败了,并显示“ panic :反射(reflect):在 slice 值上调用 reflect.Value.NumField”。我不知道该去哪里。谢谢!

最佳答案

如评论中所述,您不能在 slice 上使用 NumField,因为该方法只允许用于类型为 reflect 的 reflect.Value .结构.

因此,如果您想处理这两种类型,您需要知道传入的是哪一种。

if rv.Kind() == reflect.Struct {
changeStruct(rv)
}
if rv.Kind() == reflect.Slice {
changeSlice(rv)
}

现在,如果你想追加到一个空 slice ,你要么传递一个指向 slice 的指针,要么返回新的 slice 。

change(&b)
change(&c)

此外,为了能够初始化要附加的单个元素,您首先需要知道它的类型,要获取 slice 元素的类型,您首先要获取 slice 的 reflect.Type 和然后使用其 Elem 方法获取 slice 元素的类型。使用该类型,您可以使用 reflect.New 分配该类型的新值并将其附加到 slice 。

var elem reflect.Value

// rv is the slice
typ := rv.Type().Elem()
if typ.Kind() == reflect.Ptr {
elem = reflect.New(typ.Elem())
}
if typ.Kind() == reflect.Struct {
elem = reflect.New(typ).Elem()
}

然后要遍历 slice ,您可以使用 reflect.Value.Lenreflect.Value.Index 方法。

ln := rv.Len()
for i := 0; i < ln; i++ {
changerv(rv.Index(i))
}

代码:

func change(a interface{}) {
rv := reflect.ValueOf(a)
changerv(rv)
}

func changerv(rv reflect.Value) {
if rv.Kind() == reflect.Ptr {
rv = rv.Elem()
}

if rv.Kind() == reflect.Struct {
changeStruct(rv)
}
if rv.Kind() == reflect.Slice {
changeSlice(rv)
}
}

// assumes rv is a slice
func changeSlice(rv reflect.Value) {
ln := rv.Len()
if ln == 0 && rv.CanAddr() {
var elem reflect.Value

typ := rv.Type().Elem()
if typ.Kind() == reflect.Ptr {
elem = reflect.New(typ.Elem())
}
if typ.Kind() == reflect.Struct {
elem = reflect.New(typ).Elem()
}

rv.Set(reflect.Append(rv, elem))
}

ln = rv.Len()
for i := 0; i < ln; i++ {
changerv(rv.Index(i))
}
}

// assumes rv is a struct
func changeStruct(rv reflect.Value) {
if !rv.CanAddr() {
return
}
for i := 0; i < rv.NumField(); i++ {
field := rv.Field(i)

switch field.Kind() {
case reflect.String:
field.SetString("fred")
case reflect.Int:
field.SetInt(54)
default:
fmt.Println("unknown field")
}
}
}

playground .

关于go - 如何使用反射更改结构片段的字段?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47187680/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com