通过指定类型将“具有关联类型的协议”转换为常规协议
Posted
技术标签:
【中文标题】通过指定类型将“具有关联类型的协议”转换为常规协议【英文标题】:Turn a "protocol with associated type" in to a regular protocol by specifying the type 【发布时间】:2018-12-19 11:55:56 【问题描述】:我有以下协议:
protocol PieceViewGateway
subscript(_ identifier: PieceIdentifier) -> UIView get
我在很多地方都以这样的方式使用它:
struct SomeKindOfThing
let viewGateway: PieceViewGateway
这一切都很好,非常好。
这是协议的具体实现的一个示例(还有其他实现):
struct ViewDictionaryPieceViewGateway: PieceViewGateway
let viewDictionary: [PieceIdentifier: UIView]
subscript(identifier: PieceIdentifier) -> UIView
guard let item = viewDictionary[identifier] else
fatalError("Gateway has no value for key: \(identifier)")
return item
我有几个这样的协议。另一个是PieceValueGateway
,它返回Int
,而不是UIView
。
我不想为我需要网关的各种不同“方面”实现类似ViewDictionaryPieceViewGateway
的东西。
我试图通过定义一个具有关联类型的协议来模拟网关来实现这一点:
protocol PieceAspectGateway
associatedtype Aspect
subscript(_ identifier: PieceIdentifier) -> Aspect get
然后我将PieceViewGateway
与此一致:
protocol PieceViewGateway: PieceAspectGateway
subscript(_ identifier: PieceIdentifier) -> UIView get
但是,这会产生很多编译错误:
Protocol 'PieceViewGateway' 只能用作通用约束,因为它具有 Self 或关联的类型要求
在我添加一致性PieceViewGateway: PieceAspectGateway
之前,代码很好报告了错误。例如,SomeKindOfThing
声明它有一个let viewGateway: PieceViewGateway
。
我也尝试过这样的一致性:
protocol PieceViewGateway: PieceAspectGateway where Aspect == UIView
subscript(_ identifier: PuzzlePieceIdentifier) -> UIView get
像这样:
protocol PieceViewGateway: PieceAspectGateway
typealias Aspect = UIView
subscript(_ identifier: PuzzlePieceIdentifier) -> UIView get
…但只要将PieceViewGateway
用作协议,所有这些变体都会产生相同的错误。
有什么方法可以实现吗?
谢谢。
【问题讨论】:
你能在问题中添加错误的行吗?但一般现在不能使用varName: PieceViewGateway
,必须声明泛型<T: PieceViewGateway>(varName: T)
。
【参考方案1】:
您可以使用protocol
和associatedtype
以及类型橡皮擦来创建任何类型的Gateway
。请看下文,
protocol PieceAspectGateway
associatedtype Aspect
subscript(_ identifier: PieceIdentifier) -> Aspect get
struct AnyGateway<T>: PieceAspectGateway
let dictionary: [PieceIdentifier: T]
subscript(identifier: PieceIdentifier) -> T
guard let item = dictionary[identifier] else
fatalError("Gateway has no value for key: \(identifier)")
return item
用法
let viewGateway: AnyGateway<UIView>
let viewGateways: [AnyGateway<UIView>] = []
let intGateway: AnyGateway<Int>
let intGateways: [AnyGateway<Int>] = []
let stringGateway: AnyGateway<String>
let stringGateways: [AnyGateway<String>] = []
【讨论】:
嗨——谢谢。我通常有点理解为什么需要擦除类型。在这种情况下,我不得不说我很困惑。不过还是谢谢你的回答!【参考方案2】:你想要完成的事情很简单:
protocol PieceAspectGateway
associatedtype Aspect
subscript(_ identifier: PieceIdentifier) -> Aspect get
所以你有这个协议。当你想继承它并使用更具体的类型时,你可以使用泛型约束:
protocol IntPieceAspectGateway: PieceAspectGateway where Aspect == Int
...
【讨论】:
当您尝试像let a: [IntPieceAspectGateway] = []
一样声明array
时,这将再次导致相同的error
当然会。如果要使用泛型集合,则必须使用具体类型。在 Swift 中,无法使用具有关联类型的协议作为泛型参数。所以你必须创建一个符合它的结构或类
是的,但这是 OP 已经拥有的。他想找到一种不创建n
数量的具体类型的方法。
我向你保证,它不会。这是我尝试的第二件事(在尝试PieceViewGateway
中的typealias Aspect = UIView
之后。它不会使PieceViewGateway
成为您可以传递的“正常”协议。它仍然是一个类型约束,作为具有关联类型的协议PieceAspectGateway
是。
@Benjohn 当我说会的时候,我正在回答卡姆兰。是的,我知道它会在不创建具体类型的情况下给您同样的错误。在卡姆兰的回答中,我认为不需要该协议。这样你就可以创建一个带有泛型参数的基类,协议是没用的,因为你可以在基类中声明这些方法。以上是关于通过指定类型将“具有关联类型的协议”转换为常规协议的主要内容,如果未能解决你的问题,请参考以下文章