繁体   English   中英

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

[英]Get pointers to all fields of a struct dynamically using reflection

我正在尝试为 golang 构建一个简单的 orm 层。 这将采用一个结构并生成cols []然后可以将其传递给 sql function rows.Scan(cols...)它采用结构中与它在结果集中找到的每一列相对应的字段指针

这是我的示例结构


type ExampleStruct struct {
    ID        int64          `sql:"id"`
    aID    string         `sql:"a_id"`
    UserID    int64          `sql:"user_id"`

这是我的通用 ORM function

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
}


一旦此方法正常工作,我可以使用它生成的 map 根据我在 rows() 中获得的 column_names 创建一个有序的 sql 指针列表 object 但是我在.Addr()方法调用中遇到以下错误

panic: reflect.Value.Addr of unaddressable value [recovered]
    panic: reflect.Value.Addr of unaddressable value

不可能这样做吗? 同样在理想情况下,我希望该方法采用接口而不是 *ExampleStruct,以便它可以在不同的数据库模型中重用。

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

当传递给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{}))
}

Output(在Go 游乐场试试):

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
}

这将是 output(在Go 游乐场上试试):

map[a_id:0xc00007e008 id:0xc00007e000 user_id:0xc00007e018]
map[x:0xc000018060 y:0xc000018068]

关于反射的深入介绍,请阅读博文: The Laws of Reflection

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM