为了账号安全,请及时绑定邮箱和手机立即绑定

使用反射动态获取指向结构的所有字段的指针

使用反射动态获取指向结构的所有字段的指针

Go
慕婉清6462132 2023-03-07 11:06:35
我正在尝试为 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,以便它可以在不同的数据库模型中重用。
查看完整描述

1 回答

?
米脂

TA贡献1836条经验 获得超3个赞

该错误表明您想要获取其地址的值是不可寻址的。这是因为即使您将指针传递给GetSqlColumnToFieldMap(),您也会立即取消引用它并稍后使用非指针值。


这个值被包装在一个interface{}when passed to中reflect.ValueOf(),并且包装在接口中的值是不可寻址的。


您不能取消引用指针,而是使用Type.Elem()andValue.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()返回包含在reflect.Value. 要“展开”指针,请使用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]


查看完整回答
反对 回复 2023-03-07
  • 1 回答
  • 0 关注
  • 61 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信