一尘不染

斯威夫特:是否可以在协议中添加协议扩展?

swift

可以说我有两个协议:

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

阅读 490

收藏
2020-07-07

共1个答案

一尘不染

似乎有两个用例说明为什么您想做自己正在做的事情。在第一个用例中,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并发送给另一个接受in的函数MyPcol

要注意的另一件事是,如果要向您传递的对象TheirPcol,则必须TheirPcolWrapper先将其转换为显式类型,然后才能创建实例。这是由于Swift的一些泛型限制。因此,这样的对象可以作为替代方案:

struct TheirPcolWrapper: MyPcol {
    var object: MyPcol

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

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

但是,对于一个大型项目,这两者都可能很快变得混乱。

使用子协议扩展单个对象

另一个非理想的解决方案是扩展您知道符合TheirPcol并希望支持的每个对象。例如,假设您知道ObjectA并且ObjectB符合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的注释

您应该检查条件一致性,这是一个包含在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元素集合成为Equatable,这是可以充分利用的强大功能。为协议扩展引入条件一致性会加剧重叠一致性的问题,因为说上述协议扩展的存在并不合理,这意味着没有任何符合Collection的类型可以声明其自身对Equatable,有条件或其他条件的一致性。

虽然我意识到您并不是在要求具备 条件 一致性的功能,但这是我在讨论将协议扩展为符合其他协议时最能找到的东西。

2020-07-07