Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/go/7.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Reflection 使用反射和结构构建通用处理程序函数_Reflection_Go - Fatal编程技术网

Reflection 使用反射和结构构建通用处理程序函数

Reflection 使用反射和结构构建通用处理程序函数,reflection,go,Reflection,Go,我在构建一个可以动态使用参数化结构的函数时遇到了一些困难。出于这个原因,我的代码有20多个类似的函数,除了基本上使用的一种类型。我的大部分经验都是使用Java的,我只会开发基本的泛型函数,或者使用普通对象作为函数的参数(以及从那时起的反射)。我需要类似的东西,用围棋 我有几种类型,如: // The List structs are mostly needed for json marshalling type OrangeList struct { Oranges []Orange }

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

我有几种类型,如:

// 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或框架,但这不是重点。我想学习如何构建泛型函数,以处理我所有不同类型的类似函数

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


因此,嗯,我需要给出列表类型和香草类型作为参数,然后分别构建其中一个,我的其余逻辑可能很容易修复。

结果是有一个
sqlx.StructScan(rows,&destSlice)
函数,它将在给定适当类型的切片的情况下执行内部循环。
sqlx
文档引用了缓存反射操作的结果,因此与编写反射操作相比,它可能有一些额外的优化

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

一般来说,反射是冗长而缓慢的。有时候,这是完成某件事的最好或唯一的方法,但当你可以的时候,寻找一种不用它完成任务的方法往往是值得的

因此,如果它在你的应用程序中起作用,你也可以直接将
转换为JSON,而无需经过中间结构。下面是一个示例程序(当然需要
sqlite3
),它将
sql.Rows
转换为
map[string]string
,然后转换为JSON。(注意,它不会尝试处理
NULL
,不会将数字表示为JSON数字,也不会处理任何不适合
map[string]字符串的内容


理论上,通过每次不重用相同的
rowMap
,并使用
json.Encoder
将每一行的json追加到缓冲区,您可以构建此功能以减少分配。您可以更进一步,根本不使用
行映射
,只使用名称和值的列表。我应该说,我没有将速度与基于<代码>反射的方法进行比较,尽管我知道<代码>反射足够慢,如果你能忍受这两种策略,可能值得进行比较。

有趣的问题,有多种答案:1)如何修复<代码>反射代码,2)如何将行以更干燥的方式保存到不同类型的对象(即使不直接调用
reflect
),3)如何将
序列化为JSON(有或没有保存到中间的对象)。对于第一个问题,我认为将
&target
替换为
target.Interface()
将为您提供一个
接口{}
,表示
*橙色
,我认为
结构扫描
可以接受。
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
}
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)
}