【问题标题】:Using reflection to populate a pointer to a struct使用反射填充指向结构的指针
【发布时间】:2019-12-19 07:30:00
【问题描述】:

我想遍历结构中的字段并提示字符串字段的字符串值,递归地为指向结构的字段执行此操作。

目前这是我尝试过的,但尝试在指针的字符串字段中设置此值时出现错误。

package main

import (
    "fmt"
    "reflect"
)

type Table struct {
    PK *Field
}

type Field struct {
    Name string
}


func main() {
    PopulateStruct(&Table{})
}

func PopulateStruct(a interface{}) interface {} {
    typeOf := reflect.TypeOf(a)
    valueOf := reflect.ValueOf(a)
    for i := 0; i < typeOf.Elem().NumField(); i++ {
        switch typeOf.Elem().Field(i).Type.Kind() {
        case reflect.String:
            fmt.Print(typeOf.Elem().Field(i).Name)
            var s string
            fmt.Scanf("%s", &s)
            valueOf.Elem().Field(i).SetString(s)
        case reflect.Ptr:
            ptr := reflect.New(valueOf.Elem().Field(i).Type())
            PopulateStruct(ptr.Elem().Interface())
            valueOf.Elem().Field(i).Set(ptr)
        }
    }
}

期望返回值包含一个带有指针字符串字段集的初始化结构。

设置指针的字符串字段时出错。

恐慌:反映:在零值上调用 reflect.Value.Field

【问题讨论】:

  • 指针是nil吗?如果是这样,您需要在遍历之前使用一个值对其进行初始化。
  • 更新问题以显示您的测试用例。

标签: go reflection


【解决方案1】:

我将您的代码原样放到 Go Playground 中,但它没有构建,因为 PopulateStruct 被声明为返回 interface{} 但实际上并没有返回任何内容。删除声明的返回类型会产生您提到的恐慌。

这是因为在外部PopulateStruct 调用的入口处,您有一个有效指针,指向一个零值Table。一个零值Table 有一个元素:其中有一个*Field 类型的零指针。因此,您的循环运行一次并如您预期的那样找到reflect.Ptr。添加更多诊断打印消息有助于了解发生了什么:

fmt.Printf("PopulateStruct: I have typeOf=%v, valueOf=%v\n", typeOf, valueOf)
for i := 0; i < typeOf.Elem().NumField(); i++ {
    switch typeOf.Elem().Field(i).Type.Kind() {
// ... snipped some code ...
    case reflect.Ptr:
        ptr := reflect.New(valueOf.Elem().Field(i).Type())
        fmt.Println("after allocating ptr, we have:", ptr.Type(), ptr,
            "but its Elem is:", ptr.Elem().Type(), ptr.Elem())

打印出来:

PopulateStruct: I have typeOf=*main.Table, valueOf=&{<nil>}
after allocating ptr, we have: **main.Field 0x40c138 but its Elem is: *main.Field <nil>

鉴于PopulateStruct 本身的构造方式,我们必须在调用PopulateStruct 之前实际分配一个真正的Field 实例现在。我们可以这样做:

        p2 := ptr.Elem()
        ptr.Elem().Set(reflect.New(p2.Type().Elem()))

(code borrowed from json.Unmarshal)。现在我们可以填写这个Field,它有一个名为Name,类型为String的字段。

在我看来,这里的总体策略并不是那么好:填充可能应该采用通用指针,而不是专门指向struct 的指针。然后,您可以在 json unmarshaller 中模拟 indirect 函数。但是,添加这两行(创建目标对象并让分配的指针指向它)足以使您现有的代码运行。

(或者,您可以从头开始创建并返回整个实例,在这种情况下,您所需要的只是类型 - 但我假设您有一个模式,其中只有 some 字段无。)

Here's the complete Go Playground example.我做了一些其他的更改,因为在使用 Playground 时没有任何可扫描的内容。

【讨论】:

    猜你喜欢
    • 2018-11-03
    • 2021-01-18
    • 1970-01-01
    • 2021-01-26
    • 1970-01-01
    • 1970-01-01
    • 2013-02-26
    • 1970-01-01
    相关资源
    最近更新 更多