web-dev-qa-db-ja.com

Swift:プロトコル拡張をプロトコルに追加することは可能ですか?

2つのプロトコルがあるとします。

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

私がやりたいのは、「TheirPcol」のプロトコル拡張機能を作成することです。これにより、「TheirPcol」に準拠するものすべてに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()
_

これのキッカーは、「TheirPcol」、「TheirStruct」はすべて、私が制御しない外部APIによって処理されるということです。したがって、インスタンス「inst」が渡されます。

これはできますか?それとも私はこのようなことをしなければならないでしょうか?

_struct TheirStruct:TheirPcol {}
let inst = TheirStruct() as! MyPcol
inst.extraFunc()
_
17
drekka

自分がしていることをしたい理由が2つあるようです。最初のユースケースでは、Swiftを使用すると、やりたいことを実行できますが、2番目のユースケースではあまりきれいにできません。2つ目のカテゴリに当てはまると思いますが、両方を通過します。

TheirPcolの機能の拡張

これを行う理由の1つは、単にTheirPcolに追加の機能を提供することです。コンパイラエラーが言うように、他のプロトコルに準拠するようにSwiftプロトコルを拡張することはできません。ただし、TheirPcolを拡張することはできます。

_extension TheirPcol {
    func extraFunc() { /* do magic */ }
}
_

ここでは、TheirPcolに準拠するすべてのオブジェクトにメソッドextraFunc()を与え、デフォルトの実装を与えています。これにより、TheirPcolに準拠するオブジェクトの機能を拡張するタスクが実行され、独自のオブジェクトにも適用したい場合は、オブジェクトをTheirPcolに準拠させることができます。ただし、多くの状況では、MyPcolをプライマリプロトコルとして維持し、TheirPcolMyPcolに準拠しているものとして扱います。残念ながら、Swiftは現在、他のプロトコルへの適合を宣言するプロトコル拡張をサポートしていません。

TheirPcolオブジェクトをMyPcolのように使用する

実際にMyPcolの存在が本当に必要なユースケース(ほとんどの場合はユースケース)では、私が認識している限り、目的の操作を行うための明確な方法はまだありません。ここにいくつかの機能しているが理想的ではない解決策があります:

TheirPcolのラッパー

面倒になる可能性のあるアプローチの1つは、次のようなstructまたはclassを使用することです。

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

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

既存のオブジェクトインスタンスをMyPcolに準拠させる必要がある場合は、例のように、キャストの代わりにこの構造体を理論的に使用できます。または、MyPcolをジェネリックパラメーターとして受け入れる関数がある場合は、TheirPcolを取り込んでTheirPcolWrapperに変換し、それをMyPcolを受け取る他の関数。

もう1つ注意すべき点は、TheirPcolのオブジェクトが渡されている場合、最初に明示的な型にキャストしないとTheirPcolWrapperインスタンスを作成できないことです。これは、Swiftのいくつかのジェネリックの制限によるものです。したがって、このようなオブジェクトは代わりになる可能性があります。

_struct TheirPcolWrapper: MyPcol {
    var object: MyPcol

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

これは、与えられたTheirPcolWrapperの明示的なタイプを知らなくても、TheirPcolインスタンスを作成できることを意味します。

ただし、大規模なプロジェクトの場合、これらの両方が非常に速く混乱する可能性があります。

子プロトコルを使用して個々のオブジェクトを拡張する

さらに別の理想的でない解決策は、TheirPcolに準拠していることがわかっていて、サポートしたいことがわかっている各オブジェクトを拡張することです。たとえば、ObjectAおよびObjectBTheirPcolに準拠していることがわかっているとします。以下のように、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 {}
_

残念ながら、このアプローチは、サポートしたいオブジェクトが多数ある場合、またはオブジェクトがどうなるかを事前に把握できない場合は機能しません。 type(of:)を使用してメタタイプを取得することはできますが、指定されたTheirPcolの明示的なタイプがわからない場合にも問題になります。

Swift 4に関するメモ

Swift 4.に含めることが受け入れられた提案である Conditional conformances を確認する必要があります。具体的には、この提案は、次の拡張機能を持つ機能の概要を示しています。

_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にします。これは、有効に活用できる強力な機能です。プロトコル拡張の条件適合を導入すると、適合の重複の問題が悪化します。これは、上記のプロトコル拡張の存在が、コレクションに準拠する型が、Equatable、条件付き、またはそれ以外への独自の適合を宣言できないことを意味すると言うのは不合理です。

条件付きに準拠する機能を求めているのではないことに気づきましたが、これは準拠するように拡張されているプロトコルの議論に関して私が見つけることができる最も近いものです他のプロトコルに。

17
Matthew Seaman