Generics 在Go中作为接口通道传递事物通道

Generics 在Go中作为接口通道传递事物通道,generics,interface,go,channel,Generics,Interface,Go,Channel,我的程序有一个管道结构,我刚刚实现了一个缓存过滤器,如果已经处理过的数据版本在缓存中,它会将内容直接发送到输出 func Run(in chan downloader.ReadyDownload) chan CCFile { out := make(chan CCFile) processQueue := make(chan downloader.ReadyDownload) go cache.BypassFilter(in, processQueue, out)

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

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
}
问题是,我的程序有多个这样的地方,许多类型的结构(如此代码段中的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类型和接口的理解非常有限,这让我提出了一个问题:是不是因为它们是某个或其他东西的通道,才导致了这些类型的不兼容?我该怎么做才能让它工作?假设我有一个ReadyDownload频道。是将数据转发给函数的唯一方法,该函数使用接口{}的通道作为参数,以创建接口{}的新通道,将其传递给函数并从ReadyDownload通道读取内容并将其提供给另一个通道?

这两种类型不同:

processQueue chan ReadyDownload

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

您需要做的是使所有类型为
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页面:


将每个通道更改为
struct
通道可能在这里起作用,但一般来说,您可能希望将
struct
类型视为后续处理的接口。幸运的是,go为我们提供了许多解决方案。这里有一个

考虑这个非常简单的设置,我们希望使用
对象
结构类型作为几个接口:

// 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.
然后使用它来封装初始通道:

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

顺便说一句,问题本身最简单的解决方案可能就是将ProcessItems的所有通道都设置为自己的类型,然后在需要时将assert类型返回到自己的类型。但我想问这个问题,因为我认为从长远来看,理解Go的方式和原因更有益。将切片对象转换为切片接口不同于对象通道到接口通道,而Go例程是唯一的方法。谢谢
co := make(chan Object)
go ParseFile("file.xml", co)
Archive(ObjectToSaveable(co))