问题描述
注意:我想做与How to set a struct member that is a pointer to a string using reflection in Go相同的事情,但是要采用更通用的方式。现有的问题不能解决我的问题。
我具有要使用反射填充的不同类型字段的结构:
type MyStruct struct {
SomeInt int
SomeString string
SomeIntPtr *int
SomeStringPtr *string
}
我要写入各个字段的值是从配置存储中检索的,并解析为正确的类型,类似于此:
func getValueForField(fieldName string) interface{}
对于int
和*int
类型,该函数返回int
(包装在接口中)。对于string
和*string
,该函数针对所有类型返回string
(在接口后面),依此类推。
->请注意,它不会不返回*int
/ *string
!
现在我想将值分配给struct字段:
var field reflect.Value = reflect.ValueOf(ptrToMyStruct).Elem().Field(i)
var value interface{} = getValueForField(....)
var isPointer bool = field.Kind() == reflect.Ptr
// assign "value" to "field":
if isPointer {
// ??
field.Set(reflect.ValueOf(value).Addr()) // panic: reflect.Value.Addr of unaddressable value
} else {
field.Set(reflect.ValueOf(value)) // works
}
将这些值分配给具体类型很容易,并且可以按预期工作。但是我无法在不知道地址的情况下将int
类型(从getValueForField
返回)分配给*int
字段。而且由于我只有一个interface{}
,所以需要通过反射来完成。
以下是前往“游乐场”的链接:https://play.golang.org/p/zElEGHgx1IO
解决方法
使用reflect.New()
为字段构造一个新的指针值,从value
设置指针值,然后将此新值设置为指针字段。
例如:
type MyStruct struct {
SomeIntPtr *int
SomeStringPtr *string
}
var ms MyStruct
// Set int pointer
{
var i interface{} = 3 // of type int
f := reflect.ValueOf(&ms).Elem().FieldByName("SomeIntPtr")
x := reflect.New(f.Type().Elem())
x.Elem().Set(reflect.ValueOf(i))
f.Set(x)
}
// Set string pointer
{
var i interface{} = "hi" // of type string
f := reflect.ValueOf(&ms).Elem().FieldByName("SomeStringPtr")
x := reflect.New(f.Type().Elem())
x.Elem().Set(reflect.ValueOf(i))
f.Set(x)
}
fmt.Println("ms.SomeIntPtr",*ms.SomeIntPtr)
fmt.Println("ms.SomeStringPtr",*ms.SomeStringPtr)
这将输出(在Go Playground上尝试):
ms.SomeIntPtr 3
ms.SomeStringPtr hi
因此您的代码可能如下所示:
// assign "value" to "field":
if isPointer {
x := reflect.New(field.Type().Elem())
x.Elem().Set(reflect.ValueOf(value))
field.Set(x)
} else {
field.Set(reflect.ValueOf(value)) // works
}