gpt4 book ai didi

reflection - 将反射与结构一起使用以构建通用处理程序函数

转载 作者:IT王子 更新时间:2023-10-29 01:43:19 27 4
gpt4 key购买 nike

我在构建一个可以动态使用参数化结构的函数时遇到了一些麻烦。出于这个原因,我的代码有 20 多个类似的函数,除了基本上使用的一种类型。我的大部分经验都是使用 Java 的,我只是开发基本的通用函数,或者使用普通对象作为函数的参数(以及从那时起的反射)。我需要类似的东西,使用 Go。

我有几种类型,例如:

// The List structs are mostly needed for json marshalling
type OrangeList struct {
Oranges []Orange
}

type BananaList struct {
Bananas []Banana
}

type Orange struct {
Orange_id string
Field_1 int
// The fields are different for different types, I am simplifying the code example
}

type Banana struct {
Banana_id string
Field_1 int
// The fields are different for different types, I am simplifying the code example
}

然后我有函数,基本上针对每个列表类型:

// In the end there are 20+ of these, the only difference is basically in two types! 
// This is very un-DRY!
func buildOranges(rows *sqlx.Rows) ([]byte, error) {
oranges := OrangeList{} // This type changes
for rows.Next() {
orange := Orange{} // This type changes
err := rows.StructScan(&orange) // This can handle each case already, could also use reflect myself too
checkError(err, "rows.Scan")
oranges.Oranges = append(oranges.Oranges,orange)
}
checkError(rows.Err(), "rows.Err")
jsontext, err := json.Marshal(oranges)
return jsontext, err
}

是的,我可以更改 sql 库以使用更智能的 ORM 或框架,但这不是重点。我想学习如何构建通用函数来处理我所有不同类型的类似函数。

我已经走到这一步了,但它仍然无法正常工作(我认为目标不是预期的结构):

func buildWhatever(rows *sqlx.Rows, tgt interface{}) ([]byte, error) {
tgtValueOf := reflect.ValueOf(tgt)
tgtType := tgtValueOf.Type()
targets := reflect.SliceOf(tgtValueOf.Type())
for rows.Next() {
target := reflect.New(tgtType)
err := rows.StructScan(&target) // At this stage target still isn't 1:1 smilar struct so the StructScan fails... It's some perverted "Value" object instead. Meh.
// Removed appending to the list because the solutions for that would be similar
checkError(err, "rows.Scan")
}
checkError(rows.Err(), "rows.Err")
jsontext, err := json.Marshal(targets)
return jsontext, err
}

所以嗯,我需要将列表类型和原始类型作为参数,然后分别构建一个,我的其余逻辑可能很容易修复。

最佳答案

事实证明,有一个 sqlx.StructScan(rows, &destSlice) 函数 可以在给定适当类型的 slice 的情况下执行您的内部循环。 sqlx 文档提到了缓存反射操作的结果,所以它可能比写一个有一些额外的优化。

听起来您实际上要问的直接问题是“我如何从我的 reflect.Value 中得到 rows.StructScan 会接受的东西?”直接的答案是reflect.Interface(target);它应该返回一个代表 *Orangeinterface{},您可以直接传递给 StructScan(无需额外的 & 操作需要)。然后,我认为 targets = reflect.Append(targets, target.Indirect()) 会将你的 target 变成一个 reflect.Value 代表一个Orange 并将其附加到 slice 。 targets.Interface() 应该为您提供一个 interface{},表示 json.Marshal 理解的 []Orange .我说所有这些“应该”和“我认为”是因为我还没有尝试过那条路线。

一般来说,反射是冗长而缓慢的。有时它是完成某事的最佳或唯一方法,但通常值得寻找一种方法来完成您的任务,而无需它。

因此,如果它在您的应用中有效,您还可以将 Rows 直接转换为 JSON,而无需通过中间结构。这是一个示例程序(当然需要 sqlite3)将 sql.Rows 转换为 map[string]string 然后转换为 JSON。 (请注意,它不会尝试处理 NULL,将数字表示为 JSON 数字,或者通常处理任何不适合 map[string]string 的内容。)

package main

import (
_ "code.google.com/p/go-sqlite/go1/sqlite3"

"database/sql"
"encoding/json"
"os"
)

func main() {
db, err := sql.Open("sqlite3", "foo")
if err != nil {
panic(err)
}
tryQuery := func(query string, args ...interface{}) *sql.Rows {
rows, err := db.Query(query, args...)
if err != nil {
panic(err)
}
return rows
}
tryQuery("drop table if exists t")
tryQuery("create table t(i integer, j integer)")
tryQuery("insert into t values(?, ?)", 1, 2)
tryQuery("insert into t values(?, ?)", 3, 1)

// now query and serialize
rows := tryQuery("select * from t")
names, err := rows.Columns()
if err != nil {
panic(err)
}
// vals stores the values from one row
vals := make([]interface{}, 0, len(names))
for _, _ = range names {
vals = append(vals, new(string))
}
// rowMaps stores all rows
rowMaps := make([]map[string]string, 0)
for rows.Next() {
rows.Scan(vals...)
// now make value list into name=>value map
currRow := make(map[string]string)
for i, name := range names {
currRow[name] = *(vals[i].(*string))
}
// accumulating rowMaps is the easy way out
rowMaps = append(rowMaps, currRow)
}
json, err := json.Marshal(rowMaps)
if err != nil {
panic(err)
}
os.Stdout.Write(json)
}

理论上,您可以通过不每次都重复使用相同的 rowMap 并使用 json.Encoder 将每一行的 JSON 附加到缓冲区来构建它以减少分配.您可以更进一步,根本不使用 rowMap,只使用名称和值的列表。我应该说我没有将速度与基于 reflect 的方法进行比较,虽然我知道 reflect 足够慢,但如果你能忍受的话,可能值得比较它们两种策略。

关于reflection - 将反射与结构一起使用以构建通用处理程序函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20294044/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com