如何使用接口来允许超过 1 种结构类型以使代码更有用?

我有一个看起来像这样的结构:


type BetaKey struct {

    Id           int64  `json:"-"`

    IssuerId     int64  `json:"-"          db:"issuer_id"`

    SubjectEmail string `json:"-"          db:"subject_email"`

    IssuedTime   int64  `json:"-"          db:"issued_time"`

    ExpiryTime   int64  `json:"expiryTime" db:"expiry_time"`

    Betakey      string `json:"accessKey"`

    Description  string `json:"description"`

}

在同一个包中,我有一个返回切片的函数BetaKey:


func buildResults(query string) ([]BetaKey, error) {

    results := []BetaKey{}

    rows, err := sql.DB.Queryx(query)

    if err != nil {

        return results, err

    }

    defer rows.Close()

    for rows.Next() {

        var bk BetaKey

        err := rows.StructScan(&bk)

        if err != nil {

            return results, err

        }

        results = append(results, bk)

    }

    err = rows.Err()

    if err != nil {

        return results, err

    }

    return results, nil

}

我是否可以重写此函数,以便它接受查询字符串和BetaKeyas类型interface{},并返回一个切片,interface{}以便我可以重用代码而不是将其复制粘贴到每个包中,因为它实际上是相同的但唯一的区别是改变的结构的名称。


这可能吗?这也是建议吗?如果不是,那为什么?


繁星点点滴滴
浏览 115回答 2
2回答

摇曳的蔷薇

泛型可以用来实现这样的事情,但 Go 不支持泛型。要在 Go 中做你想做的事,你需要使用反射。您可以更改您的函数以采用 1 个附加参数,reflect.Type例如,它指定应加载各个行的值的类型。然后,您可以使用reflect.New()创建此类型的新值并获取指向它的指针。您可以使用从值中Value.Interface()获取作为类型的指针值。这个包装指针现在可以传递给.interface{}reflect.Valueinterface{}Rows.StructScan()如果您希望结果切片包含非指针值,您可以使用reflect.Indirect()来获取指向的值(以及另一个reflect.Interface()将结构值提取为interface{})。示例代码:func buildResults(query string, t reflect.Type) ([]interface{}, error) {&nbsp; &nbsp; results := []interface{}{}&nbsp; &nbsp; rows, err := sql.DB.Queryx(query)&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; return results, err&nbsp; &nbsp; }&nbsp; &nbsp; defer rows.Close()&nbsp; &nbsp; for rows.Next() {&nbsp; &nbsp; &nbsp; &nbsp; val := reflect.New(t)&nbsp; &nbsp; &nbsp; &nbsp; err := rows.StructScan(val.Interface())&nbsp; &nbsp; &nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return results, err&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; i_ := reflect.Indirect(val)&nbsp; &nbsp; &nbsp; &nbsp; result = append(result, i_.Interface())&nbsp; &nbsp; }&nbsp; &nbsp; err = rows.Err()&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; return results, err&nbsp; &nbsp; }&nbsp; &nbsp; return results, nil}它的核心是for块:val := reflect.New(t)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// A pointer to a new value (of type t)err := rows.StructScan(val.Interface()) // Pass the pointer to StructScanif err != nil {&nbsp; &nbsp; return results, err}i_ := reflect.Indirect(val)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// Dereference the pointerresult = append(result, i_.Interface()) // And add the non-pointer to the result slice以下是测试它的方法:type BetaKey struct {&nbsp; &nbsp; Id&nbsp; &nbsp;string&nbsp; &nbsp; Name string}type AlphaKey struct {&nbsp; &nbsp; Id&nbsp; &nbsp; &nbsp;string&nbsp; &nbsp; Source string}r, err := buildResults("", reflect.TypeOf(AlphaKey{}))fmt.Printf("%T %+v %v\n", r[0], r, err)r, err = buildResults("", reflect.TypeOf(BetaKey{}))fmt.Printf("%T %+v %v\n", r[0], r, err)输出:main.AlphaKey [{Id:aa Source:asource} {Id:aa Source:asource} {Id:aa Source:asource}] <nil>main.BetaKey [{Id:aa Name:aname} {Id:aa Name:aname} {Id:aa Name:aname}] <nil>在Go Playground上尝试一下。笔记:上面的解决方案将返回一个类型的值,[]interface{}其元素将是静态类型,interface{}而它们的动态类型将是reflect.Type参数指定的类型。因此,例如,如果您使用以下类型调用它:bt := reflect.TypeOf(BetaKey{})结果切片中的值将具有动态类型,BetaKey因此您可以像这样安全地键入断言它们:results, err := buildResults("some query", bt)if err == nil {&nbsp; &nbsp; for _, v := range results {&nbsp; &nbsp; &nbsp; &nbsp; key := v.(BetaKey)&nbsp; &nbsp; &nbsp; &nbsp; // key is of type BetaKey, you may use it like so&nbsp; &nbsp; }} else {&nbsp; &nbsp; // handle error}

白猪掌柜的

我用 json 写了一个小例子,而不是 sql 行。您可以尝试从此代码进行开发:package mainimport (&nbsp; &nbsp; "fmt"&nbsp; &nbsp; "reflect"&nbsp; &nbsp; "encoding/json")type A struct {&nbsp; &nbsp; AField int `json:"a"`}type B struct {&nbsp; &nbsp; BField string `json:"b"`}func build(str string, typ reflect.Type) interface{} {&nbsp; &nbsp; results := reflect.MakeSlice(reflect.SliceOf(typ), 0, 10)&nbsp; &nbsp; for i:=0; i < 5; i++ {&nbsp; &nbsp; &nbsp; &nbsp; res := reflect.New(typ)&nbsp; &nbsp; &nbsp; &nbsp; json.Unmarshal([]byte(str), res.Interface())&nbsp; &nbsp; &nbsp; &nbsp; results = reflect.Append(results, res.Elem())&nbsp; &nbsp; }&nbsp; &nbsp; return results.Interface();}func main() {&nbsp; &nbsp; a := build("{ \"a\": 15 }", reflect.TypeOf(&A{}))&nbsp; &nbsp; fmt.Printf("%T : %V\n", a, a)&nbsp; &nbsp; b := build("{ \"b\": \"my string\" }", reflect.TypeOf(&B{}))&nbsp; &nbsp; fmt.Printf("%T : %V\n", b, b)}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go