使用接口定义的回调参数,使用实现进行调用。

huangapple go评论83阅读模式
英文:

Callback parameter defined using interface, called using implementation

问题

我正在尝试做什么:

我有一个库包,其中定义了几种类型,它们都实现了给定的接口。在代码中,涉及到回调函数,而我不想在每个地方都写回调函数类型,所以我定义了一个类型。

type Foo interface {
    Bar()
}

type MyCallback func(f Foo)

type CoolFoo int

type BadFoo int

func (cf *CoolFoo) Bar(cb MyCallback) {
}

func (bf *BadFoo) Bar(cb MyCallback) {
}

然后,稍后在使用该库的客户端代码中,我想使用回调函数进行调用。如果我使用接口类型进行调用,它可以工作:

cf := &CoolFoo{}

cf.Bar(func(f packageName.Foo) {
})

但是我更希望有更加自我说明的代码,并且在我的 IDE 中有正确的类型提示,所以我尝试使用实现者类型进行调用,例如:

cf := &CoolFoo{}

cf.Bar(func(f packageName.CoolFoo) {
})

这样编译会失败,并显示以下错误:

无法将类型为 func(packageName.CoolFoo) 的函数文字用作参数传递给 cf.Bar 中的类型 packageName.MyCallback

这是不可能的吗,还是我犯了一些愚蠢的错误?我在 Go 开发方面经验不太丰富,我尝试过查找,但在这里找不到解决方案。

我找到的解决方法是将其作为 Foo 或 interface{} 传递,然后在回调函数中进行类型转换,但我希望避免这样做,因为感觉很混乱。

谢谢任何帮助!

英文:

What I'm trying to do:

I have a library package which defines a few types, all implementing a given interface. Throughout the code, there are callbacks involved, and instead of writing the callback type everywhere I defined a type for it.

type Foo interface {
	Bar()
}

type MyCallback func(f Foo)

type CoolFoo int

type BadFoo int

func (cf *CoolFoo) Bar(cb MyCallback) {
}

func (bf *BadFoo) Bar(cb MyCallback) {
}

Then later from client code using that library, I want to call using callbacks. If I call it by using the interface type it works:

cf := &CoolFoo{}

cf.Bar(func(f packageName.Foo) {
})

But I would rather have more self documenting code, and proper type hinting in my IDE, so I try to call it using the implementor type, such as this:

cf := &CoolFoo{}

cf.Bar(func(f packageName.CoolFoo) {
})

Which fails to compile, with the error:

> cannot use func literal (type func(packageName.CoolFoo)) as type
> packageName.MyCallback in argument to cf.Bar

Is it not possible, or am I making some dummy mistake ? I'm not very experienced in go dev, and I've tried looking around, but couldn't find the solution here.

What I've found is passing it as a Foo or an interface{} and then casting in the callback to what I want, but I would like to avoid it as it feels messy

Thanks for any help

答案1

得分: 4

函数签名是func(f Foo),所以必须传入的就是这个。原因很简单:如果你的方法期望一个具有这个签名的回调函数,它可以传入任何实现了Foo接口的类型。如果你传入的是一个只接受CoolFoo的函数,调用者仍然可以传入BadFoo,因为你定义的类型允许这样做。因此,编译器要求类型必须精确匹配。同样地,如果你的回调函数尝试将其转换为一个具体类型,但实际传入的类型不是它所期望的类型,那么它将失败,因为你正在做一个代码不支持的假设。

如果你的回调函数只会传入CoolFoo,也许在函数签名中应该使用这个类型。如果CoolFooBadFoo各自只会传入它们自己的类型给它们的回调函数,也许你需要两个不同的回调函数签名。如果它们真的可以互换使用,那么你描述的情况(一个只接受CoolFoo的函数)不是语言的问题,而是你设计的问题。

英文:

The function signature is func(f Foo), so that is exactly what must be passed. The reason is simple: if your method expects a callback function of that signature, it can pass in any type that implements Foo. If you passed in a function that only accepted CoolFoo, the caller could still pass in BadFoo, because the types you've defined allow it. Thus, the compiler requires that the types match precisely. Likewise, if your callback function tries to cast to a concrete type, but that isn't the type that was passed to it, it will fail, because you're making an assumption that isn't supported by the code.

If your callback will only pass in CoolFoo, maybe that should be the type you use in the signature. If CoolFoo and BadFoo will each only pass in their respective types to their callbacks, maybe you need two different callback signatures. If they really are interchangeable, then the case you describe (a function taking CoolFoo specifically) isn't a problem with the language, it's a problem with your design.

huangapple
  • 本文由 发表于 2017年6月29日 01:20:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/44808946.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定