如何在自定义类型的切片范围内

时间:2019-12-13 13:46:23

标签: go

我正在尝试为Google DataStore编写Go自定义缓存(更确切地说,是对现有缓存库之一的包装)。在缓存初始化时,它应该接受任何自定义类型的结构(带有适当定义的数据存储区),然后这将成为存储所有项目的基础。这个想法是,可以针对各种类型创建/初始化缓存,以反映特定数据存储条目( CustomEntry

的结构。

方法1 -存储 reflect.Type 并使用它。 遇到的问题-无法遍历自定义类型的切片

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 不是 []界面{}

func (cache *MyCache) CacheData(dataQuery string, dataType []interface{}) {
    if keys, err := DataStoreClient.GetAll(cache.ctx, datastore.NewQuery(dataQuery), &dataType); err != nil {
        //handle error
    } else {
        for i, dataEntry:= range slice {
        // this seems to work fine
            cache.Set(keys[i].Name, dataEntry)
        }
}

//usage: 
var dataType []CustomEntry
Cache.CacheData("Person", data)
// ERROR: Cannot use 'data' (type []CustomEntry) as type []interface{}

任何建议将不胜感激。

1 个答案:

答案 0 :(得分:0)

我找到了一个解决方案,并认为如果其他任何人遇到类似的问题,也值得分享。

最简单的方法是启动DataStore希望接收的结构切片,然后将指向它的指针作为参数(接口{})传递到所需的函数中。与一些取消封送功能(我曾尝试过使用JSON包)类似,DataStore能够成功将数据追加到其中。

在给定特定类型的情况下,尝试在函数中动态创建切片,然后该切片将被函数(例如DataStore客户端)接受,这可能非常困难(我还没有设法找到一种方法)。同样,传递一片接口(以便进行简单的迭代)只会使事情复杂化。

第二,为了遍历数据(例如,将其存储在缓存中),必须: (1)检索接口的基础值(即指针本身)-可以使用reflect.ValueOf(pointerInterface)来实现, (2)取消引用指针,以便我们可以访问底层的可迭代结构片段-可以通过调用.Elem()来实现, (3)使用.Index(i)方法在基础切片上进行迭代(即使基础类型是可迭代的,range也不会接受接口)。

自然地,添加一些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)
        }
    }
}