如何确定切片 interface{} 的元素类型?

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

How to determine the element type of slice interface{}?

问题

我有以下代码来将切片的长度加倍。

  1. func doubleSlice(s []int) []int {
  2. t := make([]int, len(s), (cap(s) + 1) * 2 )
  3. for i := range s {
  4. t[i] = s[i]
  5. }
  6. return t
  7. }

我想要将这个函数改成可以加倍任何类型的切片。但是我需要先知道元素的类型。

  1. func showInterfaceItem(s interface{}) interface{} {
  2. if reflect.TypeOf(s).Kind() != reflect.Slice {
  3. fmt.Println("The interface is not a slice.")
  4. return
  5. }
  6. var t interface{}
  7. newLen := reflect.ValueOf(s).Len()
  8. newCap := (cap(reflect.ValueOf(s).Cap()) + 1) * 2
  9. t = make([]reflect.TypeOf(s), newLen, newCap)
  10. return t
  11. }

reflect.TypeOf(s) 返回的是 interface{} 的类型,而不是切片元素的类型。我该如何获取切片接口的元素类型?

英文:

I have the following code to double the slice.

  1. func doubleSlice(s []int) []int {
  2. t := make([]int, len(s), (cap(s) + 1) * 2 )
  3. for i := range s {
  4. t[i] = s[i]
  5. }
  6. return t
  7. }

I want to make the func to double any type of slice. And I need to know the element type first.

  1. func showInterfaceItem(s interface{}) interface{} {
  2. if reflect.TypeOf(s).Kind() != reflect.Slice {
  3. fmt.Println("The interface is not a slice.")
  4. return
  5. }
  6. var t interface{}
  7. newLen := reflect.ValueOf(s).Len()
  8. newCap := (cap(reflect.ValueOf(s).Cap()) + 1) * 2
  9. t = make([]reflect.TypeOf(s), newLen, newCap)
  10. return t
  11. }

The reflect.TypeOf(s) return the type of interface{}, not the type of element. How can I get the element type of slice interface?

答案1

得分: 13

你可以使用reflect.TypeOf(s).Elem()来获取切片元素的类型。

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. func doubleSlice(s interface{}) interface{} {
  7. if reflect.TypeOf(s).Kind() != reflect.Slice {
  8. fmt.Println("The interface is not a slice.")
  9. return nil
  10. }
  11. v := reflect.ValueOf(s)
  12. newLen := v.Len()
  13. newCap := (v.Cap() + 1) * 2
  14. typ := reflect.TypeOf(s).Elem()
  15. t := reflect.MakeSlice(reflect.SliceOf(typ), newLen, newCap)
  16. reflect.Copy(t, v)
  17. return t.Interface()
  18. }
  19. func main() {
  20. xs := doubleSlice([]string{"foo", "bar"}).([]string)
  21. fmt.Println("data =", xs, "len =", len(xs), "cap =", cap(xs))
  22. ys := doubleSlice([]int{3, 1, 4}).([]int)
  23. fmt.Println("data =", ys, "len =", len(ys), "cap =", cap(ys))
  24. }

输出结果将会是:

  1. data = [foo bar] len = 2 cap = 6
  2. data = [3 1 4] len = 3 cap = 8

Go Playground 上可以进行测试。

英文:

You can use reflect.TypeOf(s).Elem()
to get the type of element of slice.

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. func doubleSlice(s interface{}) interface{} {
  7. if reflect.TypeOf(s).Kind() != reflect.Slice {
  8. fmt.Println("The interface is not a slice.")
  9. return nil
  10. }
  11. v := reflect.ValueOf(s)
  12. newLen := v.Len()
  13. newCap := (v.Cap() + 1) * 2
  14. typ := reflect.TypeOf(s).Elem()
  15. t := reflect.MakeSlice(reflect.SliceOf(typ), newLen, newCap)
  16. reflect.Copy(t, v)
  17. return t.Interface()
  18. }
  19. func main() {
  20. xs := doubleSlice([]string{"foo", "bar"}).([]string)
  21. fmt.Println("data =", xs, "len =", len(xs), "cap =", cap(xs))
  22. ys := doubleSlice([]int{3, 1, 4}).([]int)
  23. fmt.Println("data =", ys, "len =", len(ys), "cap =", cap(ys))
  24. }

The output will be:

  1. data = [foo bar] len = 2 cap = 6
  2. data = [3 1 4] len = 3 cap = 8

Check it in: Go Playground

答案2

得分: 0

这在Go语言中是可行的,并且花了我一整天的时间来发现这个模式。

首先,我们想要获取一个切片的指针,以使gorm库满意,其类型为"*[]Obj"。在Go语言中,我们可以创建一个包装函数来实现:

  1. func makeWrapper(cap uint) interface{} {
  2. arr := make([]Sth, 0, cap)
  3. return &arr
  4. }

注意,我们不能直接引用创建的值,因为它可能是需要有一个堆栈空间来存储的内部数据。

  1. // 不起作用的示例
  2. func makeWrapper(cap uint) interface{} {
  3. return &(make([]Sth, 0, cap))
  4. }

正如之前的答案所述,reflect.MakeSlice(reflect.SliceOf(typ), 0, capacity).Interface() 返回的是interface{[]Sth}。(这里的typ是指reflect.TypeOf(Sth{}),相当于typ == reflect.TypeOf(v)

因此,我们需要创建一个返回类型为*[]Sth的对象,其中包含一个容量为capacity的切片[]Sth。在理解了目标之后,我们可以编写以下代码:

  1. package main
  2. import (
  3. "reflect"
  4. )
  5. type Sth struct {
  6. a, b string
  7. }
  8. func main() {
  9. af := createSlice(Sth{})
  10. arr := makeWrapper(10).(*[]Sth)
  11. println(reflect.TypeOf(arr).String())
  12. // 等效于 makeWrapper,但我们通过反射来实现
  13. arr = af(10).(*[]Sth)
  14. println(reflect.TypeOf(arr).String())
  15. }
  16. func makeWrapper(cap uint) interface{} {
  17. arr := make([]Sth, 0, cap)
  18. return &arr
  19. }
  20. func createSlice(v interface{}) func(int) interface{} {
  21. var typ reflect.Type
  22. if reflect.ValueOf(v).Kind() == reflect.Ptr {
  23. typ = reflect.ValueOf(v).Elem().Type()
  24. } else if reflect.ValueOf(v).Kind() == reflect.Struct {
  25. typ = reflect.TypeOf(v)
  26. } else {
  27. panic("only support instance of struct or pointer of that instance")
  28. }
  29. return func(capacity int) interface{} {
  30. // 创建保存切片的外部对象
  31. outerObj := reflect.New(reflect.SliceOf(typ))
  32. // 创建切片并保存到返回值中
  33. outerObj.Elem().Set(reflect.MakeSlice(reflect.SliceOf(typ), 0, capacity))
  34. // 获取外部对象的接口
  35. return outerObj.Interface()
  36. }
  37. }
英文:

This is doable in golang and takes me whole day to discover the pattern.

Firstly, we want to get a pointer of slice to make gorm happy, which is has type "*[]Obj". To achieve that in golang, we can create a make wrapper like so:

  1. func makeWrapper(cap uint) interface{} {
  2. arr:= make([]Sth, 0, cap)
  3. return &arr
  4. }

Notice that, we can't directly reference the maked value, which might be the book keeping data need to have a stack space to store.

  1. //Not working example
  2. func makeWrapper(cap uint) interface{} {
  3. return &(make([]Sth, 0, cap))
  4. }

And as the answer before, the reflect.MakeSlice(reflect.SliceOf(typ), 0, capacity).Interface() returns interface{[]Sth}. (the typ here is refer to reflect.TypeOf(Sth{}), which equiv to typ == reflect.TypeOf(v))

Thus we need to create a return object of *[]Sth and the value inside is a slice []Sth with capacity. After understanding the objective, we can have this code:

  1. package main
  2. import (
  3. "reflect"
  4. )
  5. type Sth struct {
  6. a, b string
  7. }
  8. func main() {
  9. af:= createSlice(Sth{})
  10. arr := makeWrapper(10).(*[]Sth)
  11. println(reflect.TypeOf(arr).String())
  12. // equiv to makeWrapper, but we do it via reflection
  13. arr = af(10).(*[]Sth)
  14. println(reflect.TypeOf(arr).String())
  15. }
  16. func makeWrapper(cap uint) interface{} {
  17. arr:= make([]Sth, 0, cap)
  18. return &arr
  19. }
  20. func createSlice(v interface{}) func(int) interface{} {
  21. var typ reflect.Type
  22. if reflect.ValueOf(v).Kind() == reflect.Ptr {
  23. typ = reflect.ValueOf(v).Elem().Type()
  24. } else if reflect.ValueOf(v).Kind() == reflect.Struct {
  25. typ = reflect.TypeOf(v)
  26. } else {
  27. panic("only support instance of struct or pointer of that instance")
  28. }
  29. return func(capacity int) interface{}{
  30. // create the outer object saves our slice
  31. outerObj:=reflect.New(reflect.SliceOf(typ))
  32. // create the slice and save it to return
  33. outerObj.Elem().Set(reflect.MakeSlice(reflect.SliceOf(typ), 0, capacity))
  34. // retrive the interface of outer object
  35. return outerObj.Interface()
  36. }
  37. }

huangapple
  • 本文由 发表于 2017年2月10日 12:02:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/42151307.html
匿名

发表评论

匿名网友

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

确定