gpt4 book ai didi

reflection - 我如何使用接口(interface)允许超过 1 种结构类型来使代码更有用?

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

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

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
}

我是否可以重写此函数,使其接收一个查询字符串以及一种 BetaKey 作为 interface{},并返回一部分interface{} 这样我就可以重用代码而不是将其复制粘贴到每个包中,因为它实际上是相同的,但唯一的区别是结构名称发生了变化。

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

最佳答案

泛型可以用来实现这样的东西,但是 Go 不支持泛型。要在 Go 中做你想做的事,你需要使用反射。

您可以将您的函数更改为采用 1 个附加参数,一个 reflect.Type例如,它指定应将各个行加载到的值类型。

然后你可以使用reflect.New()创建此类型的新值并获取指向它的指针。您可以使用 Value.Interface()reflect.Value 获取指针值作为 interface{} 类型值(value)。这个包含指针的接口(interface){}现在可以传递给Rows.StructScan()

如果你想让结果 slice 保存非指针值,你可以使用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 block :

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 上试试.

注意事项:

上述解决方案将返回一个 []interface{} 类型的值,其元素将是 static interface{} 类型,并且它们的动态类型将由 reflect.Type 参数指定。因此,例如,如果您使用类型调用它:

bt := reflect.TypeOf(BetaKey{})

结果 slice 中的值将具有动态类型 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
}

要了解有关反射的更多信息,请阅读博文:

The Go Blog: The Laws of Reflection

关于reflection - 我如何使用接口(interface)允许超过 1 种结构类型来使代码更有用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37108922/

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