Go 将通道的所有元素消耗到一个切片中

Go 将通道的所有元素消耗到一个切片中,go,channels,Go,Channels,如何从通道中消耗的所有元素中构造一个切片(就像Python那样)?我可以使用这个助手函数: func ToSlice(c chan int) []int { s := make([]int, 0) for i := range c { s = append(s, i) } return s } 但是由于这个原因,我必须为每种类型都写,不是吗?是否有实现此功能的内置函数?如果没有,我如何避免为我正在使用的每种类型复制和粘贴上述代码?您可以使ToSl

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

func ToSlice(c chan int) []int {
    s := make([]int, 0)
    for i := range c {
        s = append(s, i)
    }
    return s
}
但是由于这个原因,我必须为每种类型都写,不是吗?是否有实现此功能的内置函数?如果没有,我如何避免为我正在使用的每种类型复制和粘贴上述代码?

您可以使ToSlice()只在接口{}上工作,但您在此处保存的代码量可能会在其他地方增加复杂性

func ToSlice(c chan interface{}) []interface{} {
    s := make([]interface{}, 0)
    for i := range c {
        s = append(s, i)
    }
    return s
}
完整示例


话虽如此,正如@Volker在您展示的代码片段(哈哈)评论中所说的那样,以流式方式处理结果或在生成器处“缓冲”结果并将片段发送到频道下似乎更明智。

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

如果你真的在很多类型的通道和切片之间进行了转换,并且想要一些通用的东西,那么你可以通过反射来实现这一点,但代价是在ChanToSlice的callsite上丑陋且缺少静态类型

下面是一个完整的示例代码,演示如何使用reflect解决这个问题,并演示它在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)
}
主程序包
进口(
“fmt”
“反映”
)
//ChanToSlice从ch(必须是chan)读取所有数据,并返回
//数据切片。如果ch是“T chan”,则返回值的类型为
//[]T在返回的接口中。
//典型的调用是sl:=ChanToSlice(ch)。([]int)
func ChanToSlice(ch接口{})接口{}{
chv:=反射值(ch)
slv:=reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(ch.Elem()),0,0)
为了{
v、 ok:=chv.Recv()
如果!好的{
返回slv.Interface()
}
slv=reflect.Append(slv,v)
}
}
func main(){
ch:=制造(成交量)
go func(){
对于i:=0;i<10;i++{

ch没有用于此的内置项。这样做实际上看起来很奇怪:当您想将项目从一个goroutine逐个传递到另一个goroutine时,您将使用通道,如果项目形成一个整体并一起处理,则将使用切片。@Volker我不确定为什么这会很奇怪-如果我需要处理来自另一个goroutine的项目,该怎么办总体goroutine?我有一个goroutine可以输出数据点(通常,这些数据点会进入另一个goroutine),但我需要在两个过程中处理所有项目,所以我认为在这里使用切片是更好的选择。这个goroutine只适用于
chan接口{}
虽然如此,所以仍然可能不会做他想要的事情。@JamesHenstridge True。这是不起作用的代码:
c:=make(chan int);s:=ToSlice(c)
。要使Go成为可能,需要允许从
[]int
[]interface{}
chan int
chan interface{}的类型转换
。它不允许这种转换。@Grzegorz&James是的,当然chan也必须是一个接口{}。play链接也有。正如我所说,我不建议这样做。你可以添加最大容量来切片,这应该会稍微有点效率。不太难。s:=make([]接口{},0,len(c))