斯威夫特:是否可以在协议中添加协议扩展? [英] Swift: Is it possible to add a protocol extension to a protocol?

查看:71
本文介绍了斯威夫特:是否可以在协议中添加协议扩展?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

让我们说我有两个协议:

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

解决方案

似乎有两个用例说明为什么您可能想做自己想做的事情.在第一个用例中,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的其他函数. /p>

要注意的另一件事是,如果要向您传递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的注释

您应该查看条件一致性,已被接受纳入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,有条件的或其他条件的一致性. /p>

虽然我意识到您并没有要求具有条件的合规性,但这是我在讨论将协议扩展为符合其他协议时最能找到的东西.

Lets say I have two protocols:

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

What I want to do is to create a protocol extension for 'TheirPcol' which lets extraFunc() work on anything which conforms to 'TheirPcol'. So something like this:

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

The kicker in this is that 'TheirPcol', 'TheirStruct' are all handled by an external API which I do not control. So I'm passed the instance 'inst'.

Can this be done? Or am I going to have to do something like this:

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

解决方案

It seems there are two use-cases of why you may want to do what you are doing. In the first use-case, Swift will allow you to do what you want, but not very cleanly in the second use-case. I'm guessing you fall into the second category, but I'll go through both.

Extending the functionality of TheirPcol

One reason why you might want to do this is simply to give extra functionality to TheirPcol. Just like the compiler error says, you cannot extend Swift protocols to conform to other protocols. However, you can simply extend TheirPcol.

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

Here, you are giving all objects that conform to TheirPcol the method extraFunc() and giving it a default implementation. This accomplishes the task of extending functionality for the objects conforming to TheirPcol, and if you want it to apply to your own objects as well then you could conform your objects to TheirPcol. In many situations, however, you want to keep MyPcol as your primary protocol and just treat TheirPcol as conforming to MyPcol. Unfortunately, Swift does not currently support protocol extensions declaring conformance to other protocols.

Using TheirPcol objects as if they were MyPcol

In the use case (most likely your use case) where you really do need the separate existence of MyPcol, then as far as I am aware there is no clean way to do what you want yet. Here's a few working but non-ideal solutions:

Wrapper around TheirPcol

One potentially messy approach would be to have a struct or class like the following:

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

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

You could theoretically use this struct as an alternative to casting, as in your example, when you need to make an existing object instance conform to MyPcol. Or, if you have functions that accept MyPcol as a generic parameter, you could create equivalent functions that take in TheirPcol, then convert it to TheirPcolWrapper and send it off to the other function taking in MyPcol.

Another thing to note is if you are being passed an object of TheirPcol, then you won't be able to create a TheirPcolWrapper instance without first casting it down to an explicit type. This is due to some generics limitations of Swift. So, an object like this could be an alternative:

struct TheirPcolWrapper: MyPcol {
    var object: MyPcol

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

This would mean you could create a TheirPcolWrapper instance without knowing the explicit type of the TheirPcol you are given.

For a large project, though, both of these could get messy really fast.

Extending individual objects using a child protocol

Yet another non-ideal solution is to extend each object that you know conforms to TheirPcol and that you know you wish to support. For example, suppose you know that ObjectA and ObjectB conform to TheirPcol. You could create a child protocol of MyPcol and then explicitly declare conformance for both objects, as below:

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

Unfortunately, this approach breaks down if there are a large number of objects that you wish to support, or if you cannot know ahead of time what the objects will be. It also becomes a problem when you don't know the explicit type of a TheirPcol you are given, although you can use type(of:) to get a metatype.

A note about Swift 4

You should check out Conditional conformances, a proposal accepted for inclusion in Swift 4. Specifically, this proposal outlines the ability to have the following extension:

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

While this is not quite what you are asking, at the bottom you'll find "Alternatives considered", which has a sub-section called "Extending protocols to conform to protocols", which is much more what you're trying to do. It provides the following example:

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

Then states the following:

This protocol extension would make any Collection of Equatable elements Equatable, which is a powerful feature that could be put to good use. Introducing conditional conformances for protocol extensions would exacerbate the problem of overlapping conformances, because it would be unreasonable to say that the existence of the above protocol extension means that no type that conforms to Collection could declare its own conformance to Equatable, conditional or otherwise.

While I realize you're not asking for the ability to have conditional conformances, this is the closest thing I could find regarding discussion of protocols being extended to conform to other protocols.

这篇关于斯威夫特:是否可以在协议中添加协议扩展?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆