我如何使用接口允许超过 1 种结构类型来使代码更有用?
How can I use interfaces to allow for more than 1 struct type to make code more useable?
我有一个看起来像这样的结构:
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"`
}
在同一个包中,我有一个函数 returns 一片 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
}
我是否可以重写此函数,使其接收查询字符串以及 BetaKey
类型作为 interface{}
,以及 returns [=] 的一部分14=] 这样我就可以重用代码而不是将其复制粘贴到每个包中,因为它实际上是相同的,但唯一的区别是更改的结构名称。
这可能吗?这也是建议的吗?如果不是,那为什么?
我用 json 而不是 sql 行写了一个小例子。您可以尝试从这段代码开发:
package main
import (
"fmt"
"reflect"
"encoding/json"
)
type A struct {
AField int `json:"a"`
}
type B struct {
BField string `json:"b"`
}
func build(str string, typ reflect.Type) interface{} {
results := reflect.MakeSlice(reflect.SliceOf(typ), 0, 10)
for i:=0; i < 5; i++ {
res := reflect.New(typ)
json.Unmarshal([]byte(str), res.Interface())
results = reflect.Append(results, res.Elem())
}
return results.Interface();
}
func main() {
a := build("{ \"a\": 15 }", reflect.TypeOf(&A{}))
fmt.Printf("%T : %V\n", a, a)
b := build("{ \"b\": \"my string\" }", reflect.TypeOf(&B{}))
fmt.Printf("%T : %V\n", b, b)
}
泛型可以用来实现这样的东西,但是 Go 不支持泛型。要在 Go 中做你想做的事,你需要使用反射。
您可以更改您的函数以采用 1 个附加参数,例如 reflect.Type
指定应将各行加载到的值类型。
然后你可以使用reflect.New()
to create a new value of this type and acquire a pointer to it. You can use Value.Interface()
to obtain the pointer value as a type of interface{}
from the reflect.Value
值。这个 interface{}
包装指针现在可以传递给 Rows.StructScan()
.
如果你希望结果切片保存非指针值,你可以使用 reflect.Indirect()
来获取指向的值(和另一个 reflect.Interface()
将结构值提取为 interface{}
).
示例代码:
func buildResults(query string, t reflect.Type) ([]interface{}, error) {
results := []interface{}{}
rows, err := sql.DB.Queryx(query)
if err != nil {
return results, err
}
defer rows.Close()
for rows.Next() {
val := reflect.New(t)
err := rows.StructScan(val.Interface())
if err != nil {
return results, err
}
i_ := reflect.Indirect(val)
result = append(result, i_.Interface())
}
err = rows.Err()
if err != nil {
return results, err
}
return results, nil
}
它的核心是 for
块:
val := reflect.New(t) // A pointer to a new value (of type t)
err := rows.StructScan(val.Interface()) // Pass the pointer to StructScan
if err != nil {
return results, err
}
i_ := reflect.Indirect(val) // Dereference the pointer
result = append(result, i_.Interface()) // And add the non-pointer to the result slice
测试方法如下:
type BetaKey struct {
Id string
Name string
}
type AlphaKey struct {
Id string
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 上试试。
备注:
上述解决方案将 return 类型为 []interface{}
的值,其元素将为 static 类型 interface{}
及其动态类型将由 reflect.Type
参数指定。因此,例如,如果您使用类型调用它:
bt := reflect.TypeOf(BetaKey{})
结果切片中的值将具有动态类型 BetaKey
因此您可以像这样安全地 type assert 它们:
results, err := buildResults("some query", bt)
if err == nil {
for _, v := range results {
key := v.(BetaKey)
// key is of type BetaKey, you may use it like so
}
} else {
// handle error
}
要了解有关反射的更多信息,请阅读博客 post:
我有一个看起来像这样的结构:
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"`
}
在同一个包中,我有一个函数 returns 一片 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
}
我是否可以重写此函数,使其接收查询字符串以及 BetaKey
类型作为 interface{}
,以及 returns [=] 的一部分14=] 这样我就可以重用代码而不是将其复制粘贴到每个包中,因为它实际上是相同的,但唯一的区别是更改的结构名称。
这可能吗?这也是建议的吗?如果不是,那为什么?
我用 json 而不是 sql 行写了一个小例子。您可以尝试从这段代码开发:
package main
import (
"fmt"
"reflect"
"encoding/json"
)
type A struct {
AField int `json:"a"`
}
type B struct {
BField string `json:"b"`
}
func build(str string, typ reflect.Type) interface{} {
results := reflect.MakeSlice(reflect.SliceOf(typ), 0, 10)
for i:=0; i < 5; i++ {
res := reflect.New(typ)
json.Unmarshal([]byte(str), res.Interface())
results = reflect.Append(results, res.Elem())
}
return results.Interface();
}
func main() {
a := build("{ \"a\": 15 }", reflect.TypeOf(&A{}))
fmt.Printf("%T : %V\n", a, a)
b := build("{ \"b\": \"my string\" }", reflect.TypeOf(&B{}))
fmt.Printf("%T : %V\n", b, b)
}
泛型可以用来实现这样的东西,但是 Go 不支持泛型。要在 Go 中做你想做的事,你需要使用反射。
您可以更改您的函数以采用 1 个附加参数,例如 reflect.Type
指定应将各行加载到的值类型。
然后你可以使用reflect.New()
to create a new value of this type and acquire a pointer to it. You can use Value.Interface()
to obtain the pointer value as a type of interface{}
from the reflect.Value
值。这个 interface{}
包装指针现在可以传递给 Rows.StructScan()
.
如果你希望结果切片保存非指针值,你可以使用 reflect.Indirect()
来获取指向的值(和另一个 reflect.Interface()
将结构值提取为 interface{}
).
示例代码:
func buildResults(query string, t reflect.Type) ([]interface{}, error) {
results := []interface{}{}
rows, err := sql.DB.Queryx(query)
if err != nil {
return results, err
}
defer rows.Close()
for rows.Next() {
val := reflect.New(t)
err := rows.StructScan(val.Interface())
if err != nil {
return results, err
}
i_ := reflect.Indirect(val)
result = append(result, i_.Interface())
}
err = rows.Err()
if err != nil {
return results, err
}
return results, nil
}
它的核心是 for
块:
val := reflect.New(t) // A pointer to a new value (of type t)
err := rows.StructScan(val.Interface()) // Pass the pointer to StructScan
if err != nil {
return results, err
}
i_ := reflect.Indirect(val) // Dereference the pointer
result = append(result, i_.Interface()) // And add the non-pointer to the result slice
测试方法如下:
type BetaKey struct {
Id string
Name string
}
type AlphaKey struct {
Id string
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 上试试。
备注:
上述解决方案将 return 类型为 []interface{}
的值,其元素将为 static 类型 interface{}
及其动态类型将由 reflect.Type
参数指定。因此,例如,如果您使用类型调用它:
bt := reflect.TypeOf(BetaKey{})
结果切片中的值将具有动态类型 BetaKey
因此您可以像这样安全地 type assert 它们:
results, err := buildResults("some query", bt)
if err == nil {
for _, v := range results {
key := v.(BetaKey)
// key is of type BetaKey, you may use it like so
}
} else {
// handle error
}
要了解有关反射的更多信息,请阅读博客 post: