【问题标题】:How can I range over a channel of unknown types in golang如何在golang中覆盖未知类型的通道
【发布时间】:2017-10-27 10:15:25
【问题描述】:

我有一个函数,它接收一个字符串,并基于它创建一个基于字符串值的类型的通道。然后发送该通道以填充到另一个线程中。

在这个函数中,我想范围覆盖填充的值并使用它们。

但是,我收到一个错误:“cannot range over myChan (type interface {})”

这是我的代码示例:

func myFunc(input string) {
    var myChan interface{}

    switch input {
    case "one":
        myChan = make(chan One)
    case "two":
        myChan = make(chan Two)
    }

    go doStuff(&myChan)

    for _, item := range myChan {
        fmt.Println(item)
    }
}

请帮助我了解如何完成此操作?

编辑:对不起,我的问题不够清楚。 go doStuff(&myChan) 这行其实是这样的:

go gocsv.UnmarshalToChan(clientsFile, &myChan)

根据 gocsv UnmarshalToChan 的文档“通道必须具有具体类型。”这就是为什么我不能拥有 chan 界面{}

【问题讨论】:

  • 这样不行。它可以像 myChan 是“chan interface{}”一样工作。但更好的是创建不使用 interface{} 的通道,而是使用具有某些特定方法的接口通道。请告诉我们你为什么需要这样的东西,因为我从来不需要创建一个频道,我不知道它是什么。
  • 我收到一个 csv 文件名。不同的文件内部会有不同的结构。然后我将它发送到 gocsv.UnmarshalToChan(clientsFile, &myChan)。 gocsv 是 "github.com/gocarina/gocsv" 在范围内也不会只是 println,我会做一些处理并将其写入 db。
  • 您能否将处理和写入数据库代码作为类型的方法包含在内?喜欢Two.Process()Two.WriteDB(dbHandle)?如果你能像这样隔离它们的行为,你可以使通道成为一个带有这些方法的接口,并且范围循环可以简单地接受任何满足它的东西,在它们从通道中获取项目时调用这些方法。这也会将代码开放给依赖注入测试,因为硬编码的具体类型会使正确的单元测试变得困难或不可能。

标签: csv go channel


【解决方案1】:

myChan 变量不是通道,类型通道是这样创建的:chan interface{}

例如,如果您希望通道传递任何类型,您可以使用:

func main() {
    c := make(chan interface{}, 1)

    go func() {
        for a := range c {
            fmt.Println(a)
        }
    }()

    c <- 21
    c <- "jazz"
    c <- person{"Chet", 88}

    time.Sleep(time.Second)
}

type person struct {
    Name string
    Age int
}

完整示例:https://play.golang.org/p/BJrAHiSAaw

【讨论】:

  • 对不起,我的问题不够清楚。我已经更新了它。如果我做一个“chan interface{}”,那么我不能在 switch 语句中做“myChan = make(chan One)”
【解决方案2】:

我不是 100% 确定我理解了这个问题,但是查看您编写的代码,这就是我解决无法范围问题的方法。这将陷入僵局,因为并非所有内容都已定义......但测距不是问题或不应该是。还有 doStuff 代码,应该在通道关闭时发出信号,可能在等待组中传递,用计数器跟踪等。

package main

import (
    "fmt"
    "log"
)

// One ...
type One struct{}

// Two ...
type Two struct{}

func doStuff(*interface{}) {}

func myFunc(input string) {
    var myChan interface{}
    switch input {
    case "one":
        myChan = make(chan One)
    case "two":
        myChan = make(chan Two)
    }
    // might have to move this line of code into the switch block below...
    // see commented example
    go doStuff(&myChan)

    switch myChan.(type) {
    case chan One:
        // in this way you're not passing an interface{} but a chan or type One or Two
        // go doStuff(&myChan.(chan One))
        for item := range myChan.(chan One) {
            fmt.Println(item)
        }
    case chan Two:
        // go doStuff(&myChan.(chan One))
        for item := range myChan.(chan Two) {
            fmt.Println(item)
        }
    default:
        log.Fatalln("Unknown type entered")
    }

}

func main() {
    myFunc("one")
}

【讨论】:

  • 这就是我现在正在做的事情。我将实际上是 gocsv.UnmarshalToChan(clientsFile, &myChan) 的 doStuff 和 range 移到了第一个 switch 语句中。这对我来说是一个糟糕的解决方案,因为可能有 15 到 20 个不同的 csv 文件,我必须对所有这些文件做几乎相同的事情,所以这意味着 switch 语句中有 20 个重复代码块。我会接受你的回答 - 我有点放弃寻找 正确 的方式,但我认为它可能涉及 reflect
  • 是的,使用 20 个案例来检查会很痛苦。每个 csv 都有完全不同的结构。你必须定义 20 个不同的结构。如果您可以以编程方式规范化 csv,那么您将不需要那么多结构。或者因为它们是 csvs,所以它们是一维的。使用 []map[string]string 并忘记频道。地图中的键等于 csv 标头。
猜你喜欢
  • 2021-10-22
  • 2019-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-11
  • 2022-08-19
  • 2017-10-24
相关资源
最近更新 更多