通过反射引用嵌套结构 [英] Pass by reference nested structures through reflection

查看:175
本文介绍了通过反射引用嵌套结构的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

 类型客户端结构{
Id int
年龄int
PrimaryContact联系人
名称字符串
}

类型联系struct {
Id int
ClientId int
IsPrimary bool
电子邮件字符串
}

以上是一个示例代码;我想要实现的是以下内容:
- 使用反射循环所有客户端结构字段
- 对于每个primitive字段,使用反射设置默认值
- 对于每个结构字段使用递归应用上述步骤

问题是,当PrimaryContact字段被内省时,并且当我试图为它的任何字段设置一个值时,我最终得到以下
$ b


reflect.Value.Set using unaddressable value


如果我没有弄错的话,原因是PrimaryContact是通过值传递的,而不是通过引用传递的,所以当我在任何字段上调用Set方法时,它会改变副本上的字段值而不是实际参数。我怎样才能克服这个问题?我如何通过引用使用反射将PrimaryContact字段传递给我的方法?

解决方案



两个指针:


  • 为了设置一个结构体的字段值,你必须将它作为一个指针传递给它

  • 要获得struct字段的指针值,请使用 Value.Addr()



工作解决方案:

 包主

导入(
fmt
反映
错误


类型客户端结构{
Id int
年龄int
PrimaryContact联系
名称字符串
}

类型联系结构{
Id int
ClientId int
IsPrimary bool
电子邮件字符串
}

func SetDefault(s interface {})错误{
返回setDefaultValue(reflect.ValueOf(s))
}

func setDefaultValue(v reflect.Value)错误{

如果v.Kind()!= reflect.Ptr {
返回errors.New(不是指针值)
}

v = reflect.Indirect(v)
switch v.Kind(){
case reflect.Int:
v.SetInt(42)
case reflect.String:
v.SetString(Foo)
案例reflect.Bool:
v.SetBool(true)
案例reflect.Struct:
//遍历结构字段
for i:= 0;我< v.NumField(); i ++ {
err:= setDefaultValue(v.Field(i).Addr())
if err!= nil {
return err
}
}

default:
return errors.New(Unsupported kind:+ v.Kind()。String())

}

返回nil
}


func main(){
a:=客户端{}
err:= SetDefault(& a)
if err!= nil {
fmt.Println(Error:,err)
} else {
fmt.Printf(%+ v \ n,a)
}
}

输出:

  {Id:42 Age:42 PrimaryContact:{Id:42 ClientId:42 IsPrimary:true Email:Foo} Name:Foo} 

Playground: http://play.golang.org/p/-Mpnb7o4vl


type Client struct {
    Id                int
    Age               int
    PrimaryContact    Contact
    Name              string
}

type Contact struct {
    Id        int
    ClientId  int
    IsPrimary bool
    Email     string
}

The above is a sample code; what I am trying to achieve is the following: - loop through all Client struct fields using reflection - for each "primitive" field set a default value using reflection - for each struct field use recursion to apply the above steps

The issue is that when PrimaryContact field is introspected and when I am trying to set a value for any of its fields I end up with the following panic:

reflect.Value.Set using unaddressable value

The reason if I am not mistaken is that PrimaryContact is passed by value and not by reference so when I would call Set method on any of its fields it would change fields values on the copy and not on the actual argument. How can I overcome this issue? How I could pass PrimaryContact field to my method by reference using reflection?

解决方案

I saw it as a fun exercise to practice reflection.

Two pointers though:

  • In order to set field values of a struct, you must pass it as a pointer
  • To get the pointer value of a struct field, use Value.Addr()

Working solution:

package main

import (
    "fmt"
    "reflect"
    "errors"
)

type Client struct {
    Id                int
    Age               int
    PrimaryContact    Contact
    Name              string
}

type Contact struct {
    Id        int
    ClientId  int
    IsPrimary bool
    Email     string
}

func SetDefault(s interface{}) error {
    return setDefaultValue(reflect.ValueOf(s))
}

func setDefaultValue(v reflect.Value) error {

    if v.Kind() != reflect.Ptr {
        return errors.New("Not a pointer value")
    }

    v = reflect.Indirect(v)
    switch v.Kind() {
        case reflect.Int:
            v.SetInt(42)
        case reflect.String:
            v.SetString("Foo")
        case reflect.Bool:
            v.SetBool(true)
        case reflect.Struct:
            // Iterate over the struct fields
            for i := 0; i < v.NumField(); i++ {
                err := setDefaultValue(v.Field(i).Addr())
                if err != nil {
                    return err
                }
            }       

        default:
            return errors.New("Unsupported kind: " + v.Kind().String())

    }

    return nil  
}


func main() {
    a := Client{}
    err := SetDefault(&a)
    if err != nil {
        fmt.Println("Error: ", err)
    } else {
        fmt.Printf("%+v\n", a)
    }
}

Output:

{Id:42 Age:42 PrimaryContact:{Id:42 ClientId:42 IsPrimary:true Email:Foo} Name:Foo}

Playground: http://play.golang.org/p/-Mpnb7o4vl

这篇关于通过反射引用嵌套结构的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆