【问题标题】:Passing a channel of things as a channel of interfaces in Go在 Go 中将事物通道作为接口通道传递
【发布时间】:2014-05-02 15:38:45
【问题描述】:

我的程序有一个管道结构,我刚刚实现了一个缓存过滤器,如果已经处理的数据版本在缓存中,它会直接将内容发送到输出。

func Run(in chan downloader.ReadyDownload) chan CCFile {
    out := make(chan CCFile)
    processQueue := make(chan downloader.ReadyDownload)
    go cache.BypassFilter(in, processQueue, out)
        // writes the cached, already processed version to out if it exists
        // otherwise redirects the input to processQueue
    go process(processQueue, out)
    return out
}

问题是我的程序有多个这样的地方,并且许多类型的结构(如本 sn-p 中的 ReadyDownload 和 CCFile)正在通过通道传递。他们都实现了这个接口

type ProcessItem interface {
    Source() string
    Target() string
    Key() string
}

所以我的 BypassFilter() 函数签名如下所示:

func (c Cache) BypassFilter(in chan ProcessItem, process chan ProcessItem, bypass chan ProcessItem)

但这会带来以下错误:

cannot use in (type chan downloader.ReadyDownload) as type chan utils.ProcessItem in function argument

虽然 ReadyDownload 确实实现了 ProcessItem。例如,这没有问题:

foo := downloader.ReadyDownload{}
var bar utils.ProcessItem
bar = foo

所以,我(但)对 Go 类型和接口的理解非常有限,这让我提出了这个问题:它们是 某种东西的通道其他东西,这使得类型不兼容?我应该怎么做才能让它工作?假设我有一个ReadyDownloads 频道。是将数据转发到函数的唯一方法,假设接口的通道作为参数,创建一个新的接口通道,将其传递给函数并从 ReadyDownloads 的通道中读取内容并将它们提供给另一个频道?

【问题讨论】:

  • 顺便说一句。问题本身最简单的解决方案可能就是创建 ProcessItems 的所有通道通道,然后在需要时将事物类型断言回它们自己的类型。但我想问这个问题,因为我认为从长远来看,了解 Go 的方式和原因会更有益。

标签: generics interface go channel


【解决方案1】:

将每个频道更改为struct 频道可能在这里可行,但一般来说,您可能希望将struct 类型视为接口以便在以后进行处理。幸运的是,Go 为我们提供了许多解决方案。这是一个。

考虑这个非常简单的设置,我们希望将Object 结构类型用作多个接口:

// Get the objects
func ParseFile(fileName string, co chan Object) {
    for _, object := range DoStuff(fileName) {
        co <- object
    }
}

// Use some saving functionality that is defined elsewhere as:
func Archive(cs chan Saveable) {
    for saveable := range cs {
        saveable.Save()
    }
}

type Saveable interface {
    Save()
}

//Implement the interfaces...
func (*Object) Save() {
    fmt.Println("Naa, I'm lazy")
}

// Or some throwing functionality?
func ThrowOnTheWall(ct chan Throwable) {
    for throwable := range cs {
        throwable.Throw()
    }
}

//...

co := make(chan Object)
go ParseFile("file.xml", co)
Archive(co) // Will NOT work, co is of the wrong type.

在这里,在任何地方都使用一些chan Object 是不合适的,因为您可能想在墙上扔一些与对象不同的东西(例如,type Defecation struct {...},您也可以将其实现为Throwable。)。

您可以使用 go 例程在后台进行强制转换:

func ObjectToSaveable(from chan Object) chan Saveable {
    to := make(chan Saveable)
    go func() {
        for object := range from {
            to <- &object
        }
        close(to)
    }()
    return to
}

然后用它来封装初始通道:

co := make(chan Object)
go ParseFile("file.xml", co)
Archive(ObjectToSaveable(co))

【讨论】:

  • 将切片对象转换为切片接口与对象通道到接口通道不同,goroutine是唯一的方法。谢谢。
【解决方案2】:

这两种是不同的类型:

processQueue chan ReadyDownload

process chan ProcessItem

您可以将ReadyDownloader 值放入chan ProcessItem 类型的通道中(如果它实现了接口),但是您不能将一种通道类型转换为另一种,就像您不能转换[]T 切片一样进入[]interface{} 切片,这是另一个与此类似的常见混淆。

你需要做的就是把chan ProcessItem类型的所有通道都做成:

func Run(in chan ProcessItem) chan CCFile {
    out := make(chan CCFile)
    processQueue := make(chan ProcessItem)
    go cache.BypassFilter(in, processQueue, out)
        // writes the cached, already processed version to out if it exists
        // otherwise redirects the input to processQueue
    go process(processQueue, out)
    return out
}

要详细了解为什么会这样(对于切片,但同样适用于通道),您可以阅读以下 go-wiki 页面:

http://code.google.com/p/go-wiki/wiki/InterfaceSlice

【讨论】:

    猜你喜欢
    • 2018-06-17
    • 2016-04-11
    • 2016-06-09
    • 2014-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-26
    • 1970-01-01
    相关资源
    最近更新 更多