如何对一个类型为`interface{}`且为切片的变量进行子切片操作?

huangapple go评论83阅读模式
英文:

How to sub slice an interface{} that is a slice?

问题

datastore.GetMulti(c appengine.Context, key []*Key, dst interface{}) API允许我最多获取1000个实体。我想获取更多。

解决这个问题的一种明显的方法是创建一个包装函数mypkg.GetMulti(),它对原始参数进行子切片(key[0:1000], key[1000:2000]...),然后多次使用这些子切片调用datastore.GetMulti()

很明显如何对key []*Key进行子切片,但如何对可能是以下类型的dst interface{}进行子切片呢:

// dst必须是[]S、[]*S、[]I或[]P,其中S是某个结构体类型,I是某个接口类型,
// P是某个非接口非指针类型,使得P或*P实现了PropertyLoadSaver接口。
// 如果是[]I,每个元素必须是Get的有效dst:它必须是结构体指针或实现了PropertyLoadSaver接口。
//
// 作为特例,PropertyList是dst的无效类型,即使PropertyList是结构体的切片。
// 为了避免错误传递,它被视为无效类型,即使本意是传递[]PropertyList。
英文:

The datastore.GetMulti(c appengine.Context, key []*Key, dst interface{}) API allows me to get 1000 entities at most. I want to get more.

An obvious way to solve this generically is to create a wrapper function mypkg.GetMulti() which sub slices (key[0:1000], key[1000:2000]...) the original arguments and calls datastore.GetMulti() several times with them.

It's pretty clear how to sub slice key []*Key, but how do I sub slice dst interface{} which could be:

// dst must be a []S, []*S, []I or []P, for some struct type S, some interface
// type I, or some non-interface non-pointer type P such that P or *P
// implements PropertyLoadSaver. If an []I, each element must be a valid dst
// for Get: it must be a struct pointer or implement PropertyLoadSaver.
//
// As a special case, PropertyList is an invalid type for dst, even though a
// PropertyList is a slice of structs. It is treated as invalid to avoid being
// mistakenly passed when []PropertyList was intended.

答案1

得分: 2

由于您是调用datastore.GetMulti的调用者,该函数接受一个interface{}参数,您可以提供任何具体值作为该参数;不需要事先将其转换为空接口类型。换句话说,任何东西都实现了空接口,所以只需传递该对象即可。

func GetMulti() {
    mySlice := make([]Whatever, 3000, 3000)
    for i := 0; i < 3; i++ {
        subSlice := mySlice[i * 1000 : (i + 1) * 1000]
        datastore.GetMulti(c,k, subSlice) // 假设已定义 'c' 和 'k'
    }
}

如果mypkg.GetMulti应该是一个通用函数,也接受一个interface{}值作为参数,那么您将需要使用反射,就像以下示例中的示例一样,在其中不是使用fmt.Println打印子切片的长度,而是调用datastore.GetMulti来处理每个子切片:

package main

import "fmt"
import "reflect"

func GetMulti(i interface{}) {
    v := reflect.ValueOf(i)
    if v.Kind() != reflect.Slice {
        panic("argument not a slice")
    }
    l := v.Len()
    p := (l / 1000)
    for i := 0; i < p; i++ {
        fmt.Println(v.Slice(i*1000, (i+1)*1000).Len())
    }
    fmt.Println(v.Slice(p*1000, l).Len())

}

func main() {
    s := make([]int, 3560, 3560)
    GetMulti(s)
}
英文:

Since you are the caller of datastore.GetMulti which takes an interface{} argument, you can provide any concrete value as that argument; it doesn't need to be converted to the empty-interface type beforehand. In other words, anything and everything implements the empty interface, so just pass that thing.

func GetMulti() {
    mySlice := make([]Whatever, 3000, 3000)
    for i := 0; i &lt; 3; i++ {
        subSlice := mySlice[i * 1000 : (i + 1) * 1000]
        datastore.GetMulti(c,k, subSlice) // &#39;c&#39; and &#39;k&#39; assumed to be defined
    }
}

In case mypkg.GetMulti should be a generic function, taking an interface{} value as well, then you'll have to use reflection as in the following example where instead of fmt.Println with the length of the subslice you'd call datastore.GetMulti with each subslice:

package main

import &quot;fmt&quot;
import &quot;reflect&quot;

func GetMulti(i interface{}) {
	v := reflect.ValueOf(i)
	if v.Kind() != reflect.Slice {
		panic(&quot;argument not a slice&quot;)
	}
	l := v.Len()
	p := (l / 1000)
	for i := 0; i &lt; p; i++ {
		fmt.Println(v.Slice(i*1000, (i+1)*1000).Len())
	}
	fmt.Println(v.Slice(p*1000, l).Len())

}

func main() {
	s := make([]int, 3560, 3560)
	GetMulti(s)
}

huangapple
  • 本文由 发表于 2014年4月4日 17:59:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/22859547.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定