【问题标题】:How to use "reflect" to set interface value inside a struct of struct如何使用“反射”在结构的结构内设置接口值
【发布时间】:2021-07-24 20:49:00
【问题描述】:

尝试使用“反射”包设置接口值时遇到了麻烦。接口值实际上是在结构的结构内。在Go Playground中查看我的代码

基本上,在initProc 内部,我想将dummyAFunc 函数分配给Box 结构中的DummyA 字段

package main 

import (
    "fmt"
    "reflect"
)

type Box struct {
    Name               string
    DummyA             interface{}
}

type SmartBox struct {
    Box
}

func dummyAFunc(i int) {
    fmt.Println("dummyAFunc() is here!")
}

func initProc(inout interface{}) {
    // Using "inout interface{}", I can take any struct that contains Box struct
    // And my goal is assign dummyAFunc to dummyA in Box struct

    iType:=reflect.TypeOf(inout)
    iValue:=reflect.ValueOf(inout)
    
    fmt.Println("Type & value:", iType.Elem(), iValue.Elem()) // Type & value: *main.SmartBox &{{ <nil>}}

    e := reflect.ValueOf(inout).Elem()
    
    fmt.Println("Can set?", e.CanSet()).      // true
    fmt.Println("NumField", e.NumField())     // panic: reflect: call of reflect.Value.NumField on ptr Value ?????
    fmt.Println("NumMethod", e.NumMethod())   // NumMethod = 0
        
}

func main() {
    smartbox := new (SmartBox)
    initProc(&smartbox)
}

我是 Go 新手,我已经阅读了 The laws of Reflection,但仍然无法弄清楚。请帮忙。谢谢!

【问题讨论】:

  • new 返回一个 pointer 指向给定类型的实例,&amp; 也返回一个 pointer 指向表达式的类型它在前面。所以你实际上是在将**SmartBox 传递给initProc。只对那个值执行一次Elem 会给你*SmartBox,并且指针没有字段,只有它们指向的结构有字段,所以你不能在指针上调用NumField
  • 从上面的解决方案应该很明显,要么使用Elem().Elem(),要么只将*SmartBox而不是**SmartBox传递给initProc
  • 谢谢@mkopriva。它为像我这样的围棋新手节省了一天的时间!

标签: go reflection go-reflect


【解决方案1】:

您将**SmartBix 传递给initProc。因此,当您使用反射的 Elem() 取消引用一次时,您仍然会得到一个指针 (*Smart box)。

由于new 已经返回一个指针,只需使用:

smartbox := new (SmartBox)

// InitProc(smartbox) // **SmartBox
InitProc(smartbox) // *SmartBox

https://play.golang.org/p/j4q6aq6QL_4


编辑

要更新输入结构的DummyA 字段,您可以执行以下操作:

func initProc2(v interface{}) error {

    if reflect.TypeOf(v).Kind() != reflect.Ptr {
        return fmt.Errorf("value must be a pointer")
    }

    dv := reflect.ValueOf(v).Elem()

    if dv.Kind() != reflect.Struct {
        return fmt.Errorf("value must be a pointer to a struct/interface")
    }

    const fname = "DummyA" // lookup field name

    f := dv.FieldByName(fname)

    if !f.CanSet() {
        return fmt.Errorf("value has no field %q or cannot be set", fname)
    }

    nv := reflect.ValueOf(dummyAFunc)

    f.Set(nv)

    return nil
}

工作示例:https://play.golang.org/p/VE751GtSGEw

【讨论】:

  • 谢谢@colm.anseo。我已经对其进行了修改,使其能够访问Box play.golang.org/p/Be8wXYiOe6G 中的字段但是如何将Box's DummyA 字段值设置为dummyAFunc
  • @calvinchso 我已经用字段更新代码更新了答案。
  • 太棒了!谢谢你的例子。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-01
  • 2018-09-20
  • 1970-01-01
相关资源
最近更新 更多