通过指定类型将“具有关联类型的协议”转换为常规协议

时间:2018-12-19 11:55:57

标签: swift swift-protocols

我有以下协议:

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}
}

但是,这会产生很多编译错误:

  

协议'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用作协议,所有这些变体都会产生相同的错误。

有什么办法可以做到这一点?

谢谢。

2 个答案:

答案 0 :(得分:1)

您可以将protocolassociatedtype和类型橡皮擦一起使用来创建任何种类的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>] = []

答案 1 :(得分:-1)

您要完成的工作非常简单:

protocol PieceAspectGateway {
    associatedtype Aspect
    subscript(_ identifier: PieceIdentifier) -> Aspect {get}
}

因此您具有此协议。当您想从中继承并使用更具体的类型时,可以使用通用约束:

protocol IntPieceAspectGateway: PieceAspectGateway where Aspect == Int {
    ...
}