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