[英]Using reflection to populate a pointer to a struct
I'd like to iterate over the fields in a struct and prompt for string values to string fields, doing this recursively for fields that are pointers to structs. 我想遍历结构中的字段并提示将字符串值输入到字符串字段,然后对指向结构的字段进行递归操作。
Currently this is what I've tried, but I get an error when trying to set this value in the pointer's string field. 目前这是我尝试过的方法,但是在尝试在指针的字符串字段中设置此值时遇到错误。
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)
}
}
}
Expecting the return value to include an initialised struct with the pointers string field set. 期望返回值包括带有指针字符串字段集的初始化结构。
Getting an error when setting the pointer's string field. 设置指针的字符串字段时发生错误。
panic: reflect: call of reflect.Value.Field on zero Value 恐慌:reflect:在零值上调用reflect.Value.Field
I dropped your code as-is into the Go Playground and it doesn't build because PopulateStruct
is declared as returning interface{}
but does not actually return anything. 我将您的代码按原样放置到Go Playground中,但是由于
PopulateStruct
被声明为返回interface{}
但实际上并未返回任何内容,所以它没有构建。 Removing the declared return type produces the panic you mention. 删除声明的返回类型会引起您提到的恐慌。
This is because at entry to the outer PopulateStruct
call, you have a valid pointer, pointing to a zero-valued Table
. 这是因为在外部
PopulateStruct
调用的入口处,您具有指向零值Table
的有效指针。 A zero-valued Table
has one element: a nil pointer in it of type *Field
. 零值
Table
具有一个元素:类型为*Field
的零指针。 Your loop therefore runs once and finds a reflect.Ptr
, as you expected. 因此,循环将运行一次,并按您的期望找到一个
reflect.Ptr
。 Adding more diagnostic print messages helps see what's happening: 添加更多诊断打印消息有助于了解发生了什么:
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())
This prints: 打印:
PopulateStruct: I have typeOf=*main.Table, valueOf=&{<nil>}
after allocating ptr, we have: **main.Field 0x40c138 but its Elem is: *main.Field <nil>
Given the way PopulateStruct
itself is constructed, we must actually allocate a real Field
instance now , before calling PopulateStruct
. 给定
PopulateStruct
本身的构造方式,在调用PopulateStruct
之前,我们实际上必须现在分配一个真实的Field
实例。 We can do this with: 我们可以这样做:
p2 := ptr.Elem()
ptr.Elem().Set(reflect.New(p2.Type().Elem()))
( code borrowed from json.Unmarshal
). ( 从
json.Unmarshal
借来的代码 )。 Now we can fill in this Field
, which has one field named Name
of type String
. 现在我们可以填写该
Field
,其中有一个名为Name
的字段,类型为String
。
The overall strategy here is not that great, in my opinion: filling-in probably should take a generic pointer, not specifically a pointer-to- struct
pointer. 在我看来,这里的总体策略不是那么好:填充可能应该采用通用指针,而不是专门针对
struct
指针。 You can then emulate the indirect
function in the json unmarshaller. 然后,您可以在json解组器中模拟
indirect
函数。 However, the addition of these two lines—creating the target object and making the allocated pointer point to it—suffices to make your existing code run. 但是,添加这两行(创建目标对象并使分配的指针指向它)足以使您现有的代码运行。
(Alternatively, you could just create and return a whole instance from scratch, in which case all you need is the type—but I'm assuming you have a pattern in which only some fields are nil.) (或者,您可以只从头创建并返回整个实例,在这种情况下,您只需要类型即可,但我假设您有一个仅将某些字段设为nil的模式。)
Here's the complete Go Playground example. 这是完整的Go Playground示例。 I made a few other changes as there's nothing to scan from when using the playground.
我进行了其他一些更改,因为使用游乐场时无需扫描任何内容。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.