通过反射传递引用嵌套结构

Mih*_*i H 4 reflection go

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

type Contact struct {
    Id        int
    ClientId  int
    IsPrimary bool
    Email     string
}
Run Code Online (Sandbox Code Playgroud)

以上是示例代码;我想要实现的目标如下: - 使用反射循环遍历所有客户端结构字段 - 为每个“原始”字段使用反射设置默认值 - 对于每个结构字段使用递归来应用上述步骤

问题是,当对 PrimaryContact 字段进行内省时,并且当我尝试为其任何字段设置值时,我最终会出现以下恐慌:

使用不可寻址值的reflect.Value.Set

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

ANi*_*sus 6

我认为这是练习反思的一个有趣的练习。

不过有两点:

  • 为了设置结构体的字段值,您必须将其作为指针传递
  • 要获取结构体字段的指针值,请使用Value.Addr()

工作解决方案:

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

{Id:42 Age:42 PrimaryContact:{Id:42 ClientId:42 IsPrimary:true Email:Foo} Name:Foo}
Run Code Online (Sandbox Code Playgroud)

游乐场: http://play.golang.org/p/-Mpnb7o4vl