【问题标题】:Swift: Is it possible to add a protocol extension to a protocol?Swift:是否可以在协议中添加协议扩展?
【发布时间】:2017-02-02 03:14:25
【问题描述】:

假设我有两个协议:

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

我想做的是为“TheirPcol”创建一个协议扩展,让extraFunc() 处理任何符合“TheirPcol”的东西。所以是这样的:

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()

其中的关键是“TheirPcol”、“TheirStruct”都由我无法控制的外部 API 处理。所以我通过了实例“inst”。

这可以吗?还是我必须做这样的事情:

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

【问题讨论】:

  • 我认为扩展ThierPol 是合适的。

标签: swift protocols extension-methods


【解决方案1】:

似乎有两个用例说明您可能想要做您正在做的事情。在第一个用例中,Swift 将允许你做你想做的事,但在第二个用例中不是很干净。我猜你属于第二类,但我会通过两者。

扩展TheirPcol的功能

您可能想要这样做的一个原因仅仅是为TheirPcol 提供额外的功能。就像编译器错误所说的那样,您不能扩展 Swift 协议以符合其他协议。但是,您可以简单地扩展 TheirPcol

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

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

像使用MyPcol 一样使用TheirPcol 对象

在您确实需要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 */ }
}

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

不过,对于一个大型项目,这两者都可能很快变得一团糟。

使用子协议扩展单个对象

另一个不理想的解决方案是扩展您知道符合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 { ... }
}

虽然这不是您要问的,但您会在底部找到“已考虑的替代方案”,其中有一个名为“扩展协议以符合协议”的子部分,这比您要尝试的要多得多去做。它提供了以下示例:

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

然后陈述如下:

此协议扩展将使任何 Equatable 元素的 Collection 成为 Equatable,这是一个可以充分利用的强大功能。为协议扩展引入条件一致性会加剧一致性重叠的问题,因为说上述协议扩展的存在意味着任何符合 Collection 的类型都不能声明其自身符合 Equatable、条件或其他方式是不合理的。

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

【讨论】:

  • 感谢您抽出宝贵时间撰写如此详细的回复。它确实帮助我理解了这个问题。不幸的是,无法控制对象的类型或返回的协议限制了我的能力。幸运的是,我已经设法在代码的其他地方找到了解决方案,尽管我仍然希望能够有效地使用扩展来向符合另一个协议的协议添加功能。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-04
相关资源
最近更新 更多