web-dev-qa-db-fra.com

Swift: Est-il possible d'ajouter une extension de protocole à un protocole?

Disons que j'ai deux protocoles:

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

Ce que je veux faire, c'est créer une extension de protocole pour 'ItsPcol' qui permet à extraFunc() de travailler sur tout ce qui est conforme à 'ItsPcol'. Donc quelque chose comme ça:

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

Le coup de pied dedans est que 'ItsPcol', 'ItsStruct' sont tous gérés par une API externe que je ne contrôle pas. Je suis donc passé l'instance "inst".

Cela peut-il être fait? Ou vais-je devoir faire quelque chose comme ça:

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

Il semble qu'il y ait deux cas d'utilisation pour lesquels vous voudrez peut-être faire ce que vous faites. Dans le premier cas d'utilisation, Swift vous permettra de faire ce que vous voulez, mais pas très proprement dans le deuxième cas d'utilisation. Je suppose que vous tombez dans la deuxième catégorie, mais je vais passer par les deux.

Extension des fonctionnalités de TheirPcol

Une des raisons pour lesquelles vous voudrez peut-être le faire est simplement de donner des fonctionnalités supplémentaires à TheirPcol. Tout comme l'indique l'erreur du compilateur, vous ne pouvez pas étendre les protocoles Swift pour les conformer à d'autres protocoles. Cependant, vous pouvez simplement étendre TheirPcol.

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

Ici, vous donnez à tous les objets conformes à TheirPcol la méthode extraFunc() et lui donnez une implémentation par défaut. Cela accomplit la tâche d'étendre la fonctionnalité des objets conformes à TheirPcol, et si vous souhaitez qu'elle s'applique également à vos propres objets, vous pouvez alors conformer vos objets à TheirPcol. Dans de nombreuses situations, cependant, vous souhaitez conserver MyPcol comme protocole principal et simplement traiter TheirPcol comme conforme à MyPcol. Malheureusement, Swift ne prend actuellement pas en charge les extensions de protocole déclarant la conformité à d'autres protocoles.

Utiliser les objets TheirPcol comme s'ils étaient MyPcol

Dans le cas d'utilisation (très probablement votre cas d'utilisation) où vous avez vraiment besoin de l'existence distincte de MyPcol, alors pour autant que je sache, il n'y a pas encore de moyen propre de faire ce que vous voulez. Voici quelques solutions fonctionnelles mais non idéales:

Wrapper autour de TheirPcol

Une approche potentiellement compliquée serait d'avoir un struct ou class comme suit:

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

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

Vous pouvez théoriquement utiliser cette structure comme alternative à la conversion, comme dans votre exemple, lorsque vous devez rendre une instance d'objet existante conforme à MyPcol. Ou, si vous avez des fonctions qui acceptent MyPcol comme paramètre générique, vous pouvez créer des fonctions équivalentes qui prennent TheirPcol, puis les convertir en TheirPcolWrapper et les envoyer au autre fonction prenant MyPcol.

Une autre chose à noter est que si un objet de TheirPcol vous est transmis, vous ne pourrez pas créer une instance de TheirPcolWrapper sans le convertir au préalable en un type explicite. Cela est dû à certaines limitations génériques de Swift. Donc, un objet comme celui-ci pourrait être une alternative:

struct TheirPcolWrapper: MyPcol {
    var object: MyPcol

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

Cela signifie que vous pouvez créer une instance TheirPcolWrapper sans connaître le type explicite de TheirPcol qui vous est fourni.

Pour un grand projet, cependant, les deux pourraient devenir très rapidement désordonnés.

Extension d'objets individuels à l'aide d'un protocole enfant

Une autre solution non idéale consiste à étendre chaque objet dont vous savez qu'il est conforme à TheirPcol et que vous savez que vous souhaitez prendre en charge. Par exemple, supposons que vous savez que ObjectA et ObjectB sont conformes à TheirPcol. Vous pouvez créer un protocole enfant de MyPcol, puis déclarer explicitement la conformité pour les deux objets, comme ci-dessous:

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

Malheureusement, cette approche se décompose s'il y a un grand nombre d'objets que vous souhaitez prendre en charge, ou si vous ne pouvez pas savoir à l'avance quels seront les objets. Cela devient également un problème lorsque vous ne connaissez pas le type explicite d'un TheirPcol qui vous est fourni, bien que vous puissiez utiliser type(of:) pour obtenir un métatype.

Une note sur Swift 4

Vous devriez vérifier Conformités conditionnelles , une proposition acceptée pour inclusion dans Swift 4. Plus précisément, cette proposition décrit la possibilité d'avoir l'extension suivante:

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

Bien que ce ne soit pas tout à fait ce que vous demandez, en bas, vous trouverez "Alternatives envisagées", qui a une sous-section intitulée "Étendre les protocoles pour se conformer aux protocoles", ce qui est bien plus ce que vous essayez de faire. Il fournit l'exemple suivant:

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

Dit ensuite ce qui suit:

Cette extension de protocole rendrait toute collection d'éléments équables équitable, ce qui est une fonctionnalité puissante qui pourrait être mise à profit. L'introduction de conformités conditionnelles pour les extensions de protocole exacerberait le problème des conformités qui se chevauchent, car il serait déraisonnable de dire que l'existence de l'extension de protocole ci-dessus signifie qu'aucun type conforme à Collection ne pourrait déclarer sa propre conformité à Equatable, conditionnel ou autre.

Bien que je réalise que vous ne demandez pas la possibilité d'avoir des conformités conditionnelles , c'est la chose la plus proche que j'ai pu trouver concernant la discussion des protocoles étendus pour se conformer à d'autres protocoles.

17
Matthew Seaman