将通道的所有元素都消耗到切片中

phi*_*hag 9 go channels

如何从通道消耗的所有元素中构造切片(如Python的list那样)?我可以使用这个辅助函数:

func ToSlice(c chan int) []int {
    s := make([]int, 0)
    for i := range c {
        s = append(s, i)
    }
    return s
}
Run Code Online (Sandbox Code Playgroud)

但由于缺乏泛型,我不得不为每种类型写出来,不是吗?是否有内置函数来实现这一点?如果没有,我怎么能避免为我正在使用的每一种类型复制和粘贴上面的代码?

Ask*_*sen 5

你可以让ToSlice()只在接口{}上工作,但你在这里保存的代码量可能会让你在其他地方花费很多.

func ToSlice(c chan interface{}) []interface{} {
    s := make([]interface{}, 0)
    for i := range c {
        s = append(s, i)
    }
    return s
}
Run Code Online (Sandbox Code Playgroud)

http://play.golang.org/p/wxx-Yf5ESN上的完整示例

话虽这么说:正如@Volker在代码片段(haha)的评论中所说的那样,看起来似乎是以流式方式处理结果或者在发生器上"缓冲它们"而只是发送它切入通道.

  • 这个只能用于`chan接口{}`,所以仍然可能不会做他所追求的. (3认同)
  • 为了使它成为可能Go需要允许从`[] int`到`[] interface {}`或`chan int`到`c​​han interface {}`的类型转换.它不允许这种转换.http://golang.org/ref/spec#Conversions (2认同)

Pau*_*kin 5

如果您的代码中只有几个实例需要进行转换,那么复制几次7行代码(甚至在使用它的地方内联它,这会将它减少到4行代码,这绝对是没有错的)最易读的解决方案)。

如果您确实有大量的通道和切片类型之间的转换,并且想要通用的东西,那么可以在ChanToSlice的调用站点进行反射,而这样做的代价是丑陋且缺少静态类型。

这是完整的示例代码,说明如何使用反射解决此问题,并演示了它在int通道上的工作方式。

package main

import (
    "fmt"
    "reflect"
)

// ChanToSlice reads all data from ch (which must be a chan), returning a
// slice of the data. If ch is a 'T chan' then the return value is of type
// []T inside the returned interface.
// A typical call would be sl := ChanToSlice(ch).([]int)
func ChanToSlice(ch interface{}) interface{} {
    chv := reflect.ValueOf(ch)
    slv := reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(ch).Elem()), 0, 0)
    for {
        v, ok := chv.Recv()
        if !ok {
            return slv.Interface()
        }
        slv = reflect.Append(slv, v)
    }
}

func main() {
    ch := make(chan int)
    go func() {
        for i := 0; i < 10; i++ {
            ch <- i
        }
        close(ch)
    }()
    sl := ChanToSlice(ch).([]int)
    fmt.Println(sl)
}
Run Code Online (Sandbox Code Playgroud)