问题描述
让我们说我有两个协议:
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
周围的包装一种可能会造成混乱的方法是使用struct
或class
,如下所示:
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
并且希望支持的每个对象.例如,假设您知道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 {
// ...
}
}
然后声明以下内容:
虽然我意识到您并没有要求具有条件的合规性,但这是我在讨论将协议扩展为符合其他协议时最能找到的东西.
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:
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.
这篇关于斯威夫特:是否可以在协议中添加协议扩展?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!