Go 如何在自定义类型的切片上设置范围
我正在尝试为Google数据存储编写Go自定义缓存(更准确地说,是一个围绕现有缓存库的包装器)。在缓存初始化时,它应该接受任何自定义类型的结构(具有适当定义的数据存储字段),这将是所有存储项的基础。其思想是可以为反映特定数据存储项(CustomEntry)结构的各种类型创建/初始化缓存 方法1-存储反射。键入并使用它遇到问题-无法迭代自定义类型的切片Go 如何在自定义类型的切片上设置范围,go,Go,我正在尝试为Google数据存储编写Go自定义缓存(更准确地说,是一个围绕现有缓存库的包装器)。在缓存初始化时,它应该接受任何自定义类型的结构(具有适当定义的数据存储字段),这将是所有存储项的基础。其思想是可以为反映特定数据存储项(CustomEntry)结构的各种类型创建/初始化缓存 方法1-存储反射。键入并使用它遇到问题-无法迭代自定义类型的切片 type CustomEntry struct { Data struct { name string
type CustomEntry struct {
Data struct {
name string `datastore:"name,noindex"`
address []string `datastore:"address,noindex"`
} `datastore:"data,noindex"`
}
func (cache *MyCache) CacheData(dataQuery string, dataType reflect.Type) {
slice := reflect.MakeSlice(reflect.SliceOf(dataType), 10, 10)
if keys, err := DataStoreClient.GetAll(cache.ctx, datastore.NewQuery(dataQuery), &slice); err != nil {
//handle error
} else {
for i, dataEntry:= range slice {
// ERROR: Cannot range over 'slice' (type Value)
cache.Set(keys[i].Name, dataEntry)
}
}
//usage: Cache.CacheData("Person", reflect.TypeOf(CustomEntry{})
方法2-接受接口数组作为参数遇到问题=[]CustomEntry不是[]接口{}
如有任何建议,将不胜感激 我找到了一个解决方案,并认为如果其他人有类似的问题,可能值得分享 最简单的方法是初始化数据存储预期接收的结构片,然后将指向它的指针作为参数(接口{})传递到所需的函数中。数据存储,类似于一些解组函数(我用JSON包尝试过),将能够成功地将数据附加到其中 尝试在函数中动态创建切片(给定特定类型),然后被函数(如DataStore client)接受可能非常困难(我还没有找到一种方法)。类似地,传递一部分接口(以便于迭代)只会使事情复杂化 其次,为了对数据进行迭代(例如,将其存储在缓存中),有必要: (1) 检索接口的基础值(即指针本身)-这可以通过使用
reflect.ValueOf(pointerInterface)
实现,
(2) 取消对指针的引用,这样我们就可以访问结构的底层可重用部分-这可以通过调用.Elem()
,
(3) 使用.Index(i)
方法迭代底层切片(range
将不接受接口,即使底层类型是iterable)
当然,添加一些switch-case语句可能适合于确保捕获任何错误,而不是导致运行时死机
因此,以下代码提供了上述问题的有效解决方案:
大体上:
var data []customEntry
c.CacheData("Person",&data)
以及功能本身:
func (cache *MyCache) CacheData(dataQuery string, data interface{}) error {
if keys, err := DataStoreClient.GetAll(cache.ctx, datastore.NewQuery(dataQuery), data); err != nil {
return err
} else {
s := reflect.ValueOf(data).Elem()
for i := 0; i < s.Len(); i++ {
cache.Set(keys[i].Name, s.Index(i), 1)
}
}
}
func(cache*MyCache)CacheData(数据查询字符串,数据接口{})错误{
如果是键,err:=DataStoreClient.GetAll(cache.ctx,datastore.NewQuery(dataQuery),data);err!=nil{
返回错误
}否则{
s:=reflect.ValueOf(data.Elem())
对于i:=0;i
你查过类似的Qs吗?是的,我查过了。通常的解决方案是在迭代之前简单地键入assert,但是——据我所知——这不能通过反射来完成(当您事先不知道实际断言的是什么类型时)。这就是为什么我认为提供更多关于我的用例的上下文可能会让一些更有经验的人找到一些潜在的解决方法。Go中没有动态类型。@Zack cache或no cache,如果您希望未报告的字段被声明它们的包之外的包访问,请不要使用未报告的字段reflect.MakeSlice
返回类型为reflect.value
的值,因此将&slice
传递给GetAll
是错误的,而尝试范围覆盖切片
也是错误的。查看reflect
包的文档,了解如何从slice
获取*[]
(这是您需要传递给GetAll
)以及如何迭代reflect.Value
包含的任何片段的元素。
func (cache *MyCache) CacheData(dataQuery string, data interface{}) error {
if keys, err := DataStoreClient.GetAll(cache.ctx, datastore.NewQuery(dataQuery), data); err != nil {
return err
} else {
s := reflect.ValueOf(data).Elem()
for i := 0; i < s.Len(); i++ {
cache.Set(keys[i].Name, s.Index(i), 1)
}
}
}