[首先,如果有人对问题有更好的名称,提案就会被广泛接受。到目前为止,我还没有找到更好的名称。]
那就是问题所在。
假设我有一个协议ProtocolA
,协议ProtocolB
和协议ProtocolX
定义如下:
protocol ProtocolA {
func doSomethingA()
}
protocol ProtocolB {
func doSomethingB()
}
protocol ProtocolX {
var handler: ProtocolA { get }
}
然后我在ProtocolX的一个类中有一个正确的实现如下:
class Donald: ProtocolX {
...
var handler: ProtocolA { ... }
...
}
然后一切正常,并且正确履行了协议要求。
BUT
如果我这样实施:
class Donald: ProtocolX {
...
var handler: ProtocolA & ProtocolB
...
}
我有一个编译时问题报告我的班级Donald
不符合ProtocolX
(根据规范,要求var handler
必须符合{{1} }})。
理论上 var ProtocolA
IS符合handler
(但它也符合ProtocolA
)。
然后我的问题是:为什么编译时问题?我方面的迅速限制或概念问题?
答案 0 :(得分:1)
没有充分的理由说明它不起作用,只是编译器尚不支持。
当前,ProtocolX指定处理程序必须为ProtocolA
类型,这意味着如果您为类Donald
声明了ProtocolA
类型以外的其他内容,则该处理程序将无法实现协议的要求。
但是,您可以将处理程序指定为ProtocolA
类型,但将其设置为ProtocolA & ProtocolB
类型的属性。在这种情况下,该属性将强制转换为ProtocolA
,并且您需要进行其他强制转换才能使用ProtocolB
的属性。例如:
typealias ProtocolAB = ProtocolA & ProtocolB
class AB: ProtocolAB {
func doSomethingA() {}
func doSomethingB() {}
}
class Donald: ProtocolX {
var handler: ProtocolA {
return AB()
}
func f() {
handler.doSomethingA() // is valid
handler.doSomethingB() // is not valid without casting the handler as `ProtocolB`
}
}
答案 1 :(得分:0)
您可能可以通过扩展其中一种协议来实现另一种协议来解决该问题,从而解决该问题。
您可以使A从B继承:
protocol ProtocolA: ProtocolB {
func doSomethingA()
}
protocol ProtocolB {
func doSomethingB()
}
protocol ProtocolX {
var handler: ProtocolA { get }
}
由于A继承自B,所以B中的每个属性在A中都可用。
然后您可以在协议X中同时使用它们,因为A继承自b
选中此answer
答案 2 :(得分:0)
不幸的是,至少当前要求协议中指定的类型是准确的。但是,目前您可以执行一些解决方法。
最简单的选择是将handler
保留为ProtocolA
,但实际上存储一个ProtocolA & ProtocolB
对象。只要您不需要遵循也有var handler: ProtocolB
的协议,这是可行的。
class Donald: ProtocolX {
var abHandler: ProtocolA & ProtocolB
var handler: ProtocolA { return abHandler }
}
如果您遇到的一个更复杂的情况是您实际上需要 handler
既是ProtocolA
又是ProtocolB
,则您必须在协议:
protocol ProtocolX {
associatedtype Handler: ProtocolA
var handler: Handler { get }
}
protocol ProtocolY {
associatedtype Handler: ProtocolB
var handler: Handler { get }
}
关联类型的问题是您需要为它们指定一个具体类型,因此即使ProtocolA & ProtocolB
是变量的有效类型,也不能将其用作关联类型Handler
。因此,您要么需要将特定的具体类型设为Handler
:
struct PrintAB: ProtocolA, ProtocolB {
func doSomethingA() { print("A") }
func doSomethingB() { print("B") }
}
class Donald: ProtocolX, ProtocolY {
var handler: PrintAB
}
…或者您可以使Donald
本身是通用的:
class Donald<Handler>: ProtocolX, ProtocolY where Handler: ProtocolA & ProtocolB {
init(handler: Handler) { self.handler = handler }
var handler: Handler
}
尽管这两种方法都可以使用,但是您也将失去使用ProtocolX
作为变量类型的能力,即let someX: ProtocolX = donald
由于{{1 }}。
当然,如果您可以控制协议定义,则可以通过为处理程序使用不同的名称来避免这种情况,将问题减少到更简单的情况:
ProtocolX