使用反射动态获取指向结构的所有字段的指针
Get pointers to all fields of a struct dynamically using reflection
我正在尝试为 golang 构建一个简单的 orm 层。
这将采用一个结构并生成 cols []
然后可以将其传递给 sql 函数
rows.Scan(cols...)
它采用结构中与它在结果集中找到的每一列相对应的字段指针
这是我的示例结构
type ExampleStruct struct {
ID int64 `sql:"id"`
aID string `sql:"a_id"`
UserID int64 `sql:"user_id"`
这是我的通用 ORM 函数
func GetSqlColumnToFieldMap(model *ExampleStruct) map[string]interface{} {
typeOfModel := reflect.TypeOf(*model)
ValueOfModel := reflect.ValueOf(*model)
columnToDataPointerMap := make(map[string]interface{})
for i := 0; i < ValueOfModel.NumField(); i++ {
sql_column := typeOfModel.Field(i).Tag.Get("sql")
structValue := ValueOfModel.Field(i)
columnToDataPointerMap[sql_column] = structValue.Addr()
}
return columnToDataPointerMap
}
一旦此方法正常工作,我就可以使用它生成的映射根据我在 rows() 对象中获得的 column_names 创建一个有序的 sql 指针列表
但是我在 .Addr()
方法调用
中遇到以下错误
panic: reflect.Value.Addr of unaddressable value [recovered]
panic: reflect.Value.Addr of unaddressable value
这不可能吗?
同样在理想情况下,我希望该方法采用接口而不是 *ExampleStruct,以便它可以在不同的数据库模型中重复使用。
错误提示您想要获取其地址的值不可寻址。这是因为即使您将指针传递给 GetSqlColumnToFieldMap()
,您也会立即取消引用它并稍后使用 non-pointer 值。
这个值在传递给 reflect.ValueOf()
时被包装在 interface{}
中,并且包装在接口中的值不可寻址。
您不能解引用指针,而是使用 Type.Elem()
和 Value.Elem()
来获取元素类型和指向的值。
像这样:
func GetSqlColumnToFieldMap(model *ExampleStruct) map[string]interface{} {
t := reflect.TypeOf(model).Elem()
v := reflect.ValueOf(model).Elem()
columnToDataPointerMap := make(map[string]interface{})
for i := 0; i < v.NumField(); i++ {
sql_column := t.Field(i).Tag.Get("sql")
structValue := v.Field(i)
columnToDataPointerMap[sql_column] = structValue.Addr()
}
return columnToDataPointerMap
}
通过这个简单的更改就可以了!而且它不依赖于参数类型,您可以将其更改为 interface{}
并传递任何结构指针。
func GetSqlColumnToFieldMap(model interface{}) map[string]interface{} {
// ...
}
正在测试:
type ExampleStruct struct {
ID int64 `sql:"id"`
AID string `sql:"a_id"`
UserID int64 `sql:"user_id"`
}
type Point struct {
X int `sql:"x"`
Y int `sql:"y"`
}
func main() {
fmt.Println(GetSqlColumnToFieldMap(&ExampleStruct{}))
fmt.Println(GetSqlColumnToFieldMap(&Point{}))
}
输出(在 Go Playground 上尝试):
map[a_id:<*string Value> id:<*int64 Value> user_id:<*int64 Value>]
map[x:<*int Value> y:<*int Value>]
注意 Value.Addr()
returns the address wrapped in a reflect.Value
. To "unwrap" the pointer, use Value.Interface()
:
func GetSqlColumnToFieldMap(model interface{}) map[string]interface{} {
t := reflect.TypeOf(model).Elem()
v := reflect.ValueOf(model).Elem()
m := make(map[string]interface{})
for i := 0; i < v.NumField(); i++ {
colName := t.Field(i).Tag.Get("sql")
field := v.Field(i)
m[colName] = field.Addr().Interface()
}
return m
}
这将输出(在 Go Playground 上尝试):
map[a_id:0xc00007e008 id:0xc00007e000 user_id:0xc00007e018]
map[x:0xc000018060 y:0xc000018068]
有关反射的 in-depth 介绍,请阅读博客 post:The Laws of Reflection
我正在尝试为 golang 构建一个简单的 orm 层。
这将采用一个结构并生成 cols []
然后可以将其传递给 sql 函数
rows.Scan(cols...)
它采用结构中与它在结果集中找到的每一列相对应的字段指针
这是我的示例结构
type ExampleStruct struct {
ID int64 `sql:"id"`
aID string `sql:"a_id"`
UserID int64 `sql:"user_id"`
这是我的通用 ORM 函数
func GetSqlColumnToFieldMap(model *ExampleStruct) map[string]interface{} {
typeOfModel := reflect.TypeOf(*model)
ValueOfModel := reflect.ValueOf(*model)
columnToDataPointerMap := make(map[string]interface{})
for i := 0; i < ValueOfModel.NumField(); i++ {
sql_column := typeOfModel.Field(i).Tag.Get("sql")
structValue := ValueOfModel.Field(i)
columnToDataPointerMap[sql_column] = structValue.Addr()
}
return columnToDataPointerMap
}
一旦此方法正常工作,我就可以使用它生成的映射根据我在 rows() 对象中获得的 column_names 创建一个有序的 sql 指针列表
但是我在 .Addr()
方法调用
panic: reflect.Value.Addr of unaddressable value [recovered]
panic: reflect.Value.Addr of unaddressable value
这不可能吗? 同样在理想情况下,我希望该方法采用接口而不是 *ExampleStruct,以便它可以在不同的数据库模型中重复使用。
错误提示您想要获取其地址的值不可寻址。这是因为即使您将指针传递给 GetSqlColumnToFieldMap()
,您也会立即取消引用它并稍后使用 non-pointer 值。
这个值在传递给 reflect.ValueOf()
时被包装在 interface{}
中,并且包装在接口中的值不可寻址。
您不能解引用指针,而是使用 Type.Elem()
和 Value.Elem()
来获取元素类型和指向的值。
像这样:
func GetSqlColumnToFieldMap(model *ExampleStruct) map[string]interface{} {
t := reflect.TypeOf(model).Elem()
v := reflect.ValueOf(model).Elem()
columnToDataPointerMap := make(map[string]interface{})
for i := 0; i < v.NumField(); i++ {
sql_column := t.Field(i).Tag.Get("sql")
structValue := v.Field(i)
columnToDataPointerMap[sql_column] = structValue.Addr()
}
return columnToDataPointerMap
}
通过这个简单的更改就可以了!而且它不依赖于参数类型,您可以将其更改为 interface{}
并传递任何结构指针。
func GetSqlColumnToFieldMap(model interface{}) map[string]interface{} {
// ...
}
正在测试:
type ExampleStruct struct {
ID int64 `sql:"id"`
AID string `sql:"a_id"`
UserID int64 `sql:"user_id"`
}
type Point struct {
X int `sql:"x"`
Y int `sql:"y"`
}
func main() {
fmt.Println(GetSqlColumnToFieldMap(&ExampleStruct{}))
fmt.Println(GetSqlColumnToFieldMap(&Point{}))
}
输出(在 Go Playground 上尝试):
map[a_id:<*string Value> id:<*int64 Value> user_id:<*int64 Value>]
map[x:<*int Value> y:<*int Value>]
注意 Value.Addr()
returns the address wrapped in a reflect.Value
. To "unwrap" the pointer, use Value.Interface()
:
func GetSqlColumnToFieldMap(model interface{}) map[string]interface{} {
t := reflect.TypeOf(model).Elem()
v := reflect.ValueOf(model).Elem()
m := make(map[string]interface{})
for i := 0; i < v.NumField(); i++ {
colName := t.Field(i).Tag.Get("sql")
field := v.Field(i)
m[colName] = field.Addr().Interface()
}
return m
}
这将输出(在 Go Playground 上尝试):
map[a_id:0xc00007e008 id:0xc00007e000 user_id:0xc00007e018]
map[x:0xc000018060 y:0xc000018068]
有关反射的 in-depth 介绍,请阅读博客 post:The Laws of Reflection