Swift:是否可以在协议中添加协议扩展?

时间:2017-02-02 03:14:26

标签: swift protocols extension-methods

假设我有两个协议:

protocol TheirPcol {}
protocol MyPcol {
    func extraFunc()
}

我想要做的是为'他们的人生成一个协议扩展程序。这使得extraFunc()能够处理符合'他们的职责的任何事情。所以像这样:

extension TheirPcol : MyPcol { // Error 'Extension of protocol 'TheirPcol' cannot have an inheritance clause.
    func extraFunc() { /* do magic */}
}

struct TheirStruct:TheirPcol {}
let inst = TheirStruct()
inst.extraFunc()

这方面的标志是'他们的','他们的结构'都由我无法控制的外部API处理。所以我通过了实例'。'

可以这样做吗?或者我将不得不做这样的事情:

struct TheirStruct:TheirPcol {}
let inst = TheirStruct() as! MyPcol
inst.extraFunc()

1 个答案:

答案 0 :(得分:10)

似乎有两个用例,说明为什么你可能想做你正在做的事情。在第一个用例中,Swift将允许您执行您想要的操作,但在第二个用例中并不是非常干净。我猜你属于第二类,但我会经历这两个类别。

扩展TheirPcol

的功能

您可能希望这样做的一个原因就是为TheirPcol提供额外的功能。就像编译器错误所说的那样,你无法扩展Swift协议以符合其他协议。但是,您只需扩展TheirPcol

extension TheirPcol {
    func extraFunc() { /* do magic */ }
}

在这里,您将为方法TheirPcol提供符合​​extraFunc()的所有对象,并为其提供默认实现。这完成了扩展符合TheirPcol的对象的功能的任务,如果您希望它也适用于您自己的对象,那么您可以将对象符合TheirPcol。但是,在许多情况下,您希望将MyPcol作为主要协议,并将TheirPcol视为符合MyPcol。不幸的是,Swift目前不支持声明与其他协议一致的协议扩展。

使用TheirPcol个对象,就好像它们是MyPcol

一样

在用例(很可能是你的用例)中你确实需要单独存在MyPcol,那么据我所知,没有干净的方法来做你想要的。这是一些有效但非理想的解决方案:

TheirPcol

周围的包装

一种潜在的混乱方法是拥有structclass,如下所示:

struct TheirPcolWrapper<T: TheirPcol>: MyPcol {
    var object: T

    func extraFunc() { /* Do magic using object */ }
}

理论上,您可以使用此结构作为转换的替代方法,如您的示例中所示,当您需要使现有对象实例符合MyPcol时。或者,如果您有接受MyPcol作为通用参数的函数,则可以创建接受TheirPcol的等效函数,然后将其转换为TheirPcolWrapper并将其发送到其他函数在MyPcol

另一件需要注意的事情是,如果您传递的是TheirPcol的对象,那么您将无法创建TheirPcolWrapper实例,而无需先将其转换为显式类型。这是由于Swift的一些泛型限制。因此,像这样的对象可能是另一种选择:

struct TheirPcolWrapper: MyPcol {
    var object: MyPcol

    func extraFunc() { /* Do magic using object */ }
}

这意味着您可以在不知道给出TheirPcolWrapper的显式类型的情况下创建TheirPcol实例。

对于一个大型项目,这两个都可能非常快速地混乱。

使用子协议扩展单个对象

另一个非理想的解决方案是扩展您知道符合TheirPcol的每个对象,并且您知道您希望支持这些对象。例如,假设您知道ObjectAObjectB符合TheirPcol。您可以创建MyPcol的子协议,然后明确声明两个对象的一致性,如下所示:

protocol BridgedToMyPcol: TheirPcol, MyPcol {}

extension BridgedToMyPcol {
    func extraFunc() {
        // Do magic here, given that the object is guaranteed to conform to TheirPcol
    }
}

extension ObjectA: BridgedToMyPcol {}
extension ObjectB: BridgedToMyPcol {}

不幸的是,如果您希望支持大量对象,或者您无法提前知道对象将是什么,则此方法会中断。当您不知道给出TheirPcol的显式类型时,它也会成为问题,尽管您可以使用type(of:)来获取元类型。

关于Swift 4的说明

您应该查看Conditional conformances,一个被接受加入Swift 4的提案。具体来说,此提案概述了具有以下扩展名的能力:

extension Array: Equatable where Element: Equatable {
    static func ==(lhs: Array<Element>, rhs: Array<Element>) -> Bool { ... }
}

虽然这不是你所要求的,但在底部你会发现&#34;替代品被认为&#34;,它有一个名为&#34的子部分;扩展协议以符合协议&# 34;,这是你正在尝试做的更多。它提供了以下示例:

extension Collection: Equatable where Iterator.Element: Equatable {
    static func ==(lhs: Self, rhs: Self) -> Bool {
        // ...
    }
}

然后说明以下内容:

  

此协议扩展将使任何Equatable元素集合Equatable,这是一个可以很好地使用的强大功能。引入协议扩展的条件一致性会加剧一致性重叠的问题,因为说上述协议扩展的存在意味着没有符合Collection的类型可以声明它自己与Equatable,条件或其他方式的一致性是不合理的。 / p>

虽然我意识到你并没有要求具备条件一致性的能力,但这是关于扩展协议以符合其他协议的讨论的最接近的事情。