紧急情况:反射:接口Value上对reflect.Value.FieldByName的调用

问题描述

我的变量类型为interface{},我想使用反射更改字段的值。我有类型interface{}的变量,我想使用反射更改字段的值。我该怎么做?由于其他要求,变量必须为interface{}类型。如果变量不是interface{}类型,则所有工程都可以,否则代码将抛出

reflect: call of reflect.Value.FieldByName on interface Value

我的代码

package main

import (
    "fmt"
    "reflect"
)

func main() {
    a := struct {
        Name string
    }{}

    // works
    reflect.ValueOf(&a).Elem().FieldByName("Name").SetString("Hello")
    fmt.Printf("%#v\n",a)

    var b interface{}
    b = struct {
        Name string
    }{}
    // panics
    reflect.ValueOf(&b).Elem().FieldByName("Name").SetString("Hello")
    fmt.Printf("%#v\n",b)
}

解决方法

应用程序必须两次调用Elem()才能获取结构值:

reflect.ValueOf(&b).Elem().Elem().FieldByName("Name").SetString("Hello")

第一个调用Elem()取消了对interface{}的指针的引用。对Elem()的第二次调用获取接口中包含的值。

进行此更改后,恐慌为reflect.Value.SetString using unaddressable value

由于接口中包含的值不可寻址,因此应用程序无法直接在接口中包含的结构值上设置字段。

将struct值复制到临时变量,在临时变量中设置字段,然后将临时变量复制回接口。

var b interface{}
b = struct {
    Name string
}{}

// v is the interface{}
v := reflect.ValueOf(&b).Elem()

// Allocate a temporary variable with type of the struct.
//    v.Elem() is the vale contained in the interface.
tmp := reflect.New(v.Elem().Type()).Elem()

// Copy the struct value contained in interface to
// the temporary variable.
tmp.Set(v.Elem())

// Set the field.
tmp.FieldByName("Name").SetString("Hello")

// Set the interface to the modified struct value.
v.Set(tmp)

fmt.Printf("%#v\n",b)

Run it on the Go playground

,

接口b使用匿名结构的值初始化,因此b包含该结构的副本,并且这些值不可寻址。使用指针初始化b

var b interface{}
    b = &struct {
        Name string
    }{}
    reflect.ValueOf(b).Elem().FieldByName("Name").SetString("Hello")