Swift: vérifie si le type générique est conforme au protocole

J’ai un protocole que j’ai défini ainsi:

protocol MyProtocol { ... } 

J’ai aussi une structure générique:

 struct MyStruct  { ... } 

Enfin, j’ai une fonction générique:

 func myFunc  (s: MyStruct) -> T? { ... } 

Je voudrais tester à l’intérieur de la fonction si le type T est conforme à MyProtocol. J’aimerais essentiellement pouvoir faire (~ pseudocode):

 let conforms = T.self is MyProtocol 

Mais cela génère une erreur de compilation:

 error: cannot downcast from 'T.Type' to non-@objc protocol type 'MyProtocol' let conforms = T.self is MyProtocol ~~~~~~ ^ ~~~~~~~~~~ 

J’ai aussi essayé des variantes, comme T.self is MyProtocol.self , T is MyProtocol , et en utilisant == au lieu de. Jusqu’à présent, je n’ai rien trouvé. Des idées?

Un peu tard mais vous pouvez tester si quelque chose répond au protocole avec as ? tester:

 if let currentVC = myViewController as? MyCustomProtocol { //currentVC responds to the MyCustomProtocol protocol =] } 

EDIT: un peu plus court:

 if let _=self as? MyProtocol { // match } 

Et en utilisant un garde

 guard let _=self as? MyProtocol else { // doesn't match return } 

Je dois dire que @Alex veut vérifier si le type T est conforme au protocole plutôt que s . Et certains répondeurs n’ont pas vu clairement.

Vérifiez que le type T est conforme au protocole comme ceci:

 if let _ = T.self as? MyProtocol.Type { // T conform MyProtocol } 

ou

 if T.self is MyProtocol.Type { // T conform MyProtocol } 

La réponse la plus simple est: ne le faites pas. Utilisez plutôt des surcharges et des contraintes, et déterminez tout au début de la compilation plutôt que de tester les choses dynamicment à l’exécution. La vérification du type d’exécution et les génériques à la compilation sont comme le steak et la crème glacée – les deux sont bien mais leur mélange est un peu bizarre.

Considérez quelque chose comme ceci:

 protocol MyProtocol { } struct MyStruct  { let val: T } func myFunc(s: MyStruct) -> T? { return s.val } func myFunc(s: MyStruct) -> T? { return nil } struct S1: MyProtocol { } struct S2 { } let m1 = MyStruct(val: S1()) let m2 = MyStruct(val: S2()) myFunc(m1) // returns an instance of S1 myFunc(m2) // returns nil, because S2 doesn't implement MyProtocol 

L’inconvénient est que vous ne pouvez pas établir dynamicment si T prend en charge un protocole à l’exécution:

 let o: Any = S1() let m3 = MyStruct(val: o) myFunc(m3) // will return nil even though o // does actually implement MyProtocol 

Mais en toute honnêteté, avez-vous vraiment besoin de faire cela dans votre fonction générique? Si vous n’êtes pas certain de la nature de quelque chose, la meilleure option est peut-être de l’inventer plutôt que de la reporter à plus tard et de l’insérer dans une fonction générique pour la découvrir.

Vous pouvez également tirer parti de la correspondance des modèles de boîtiers de commutation , si vous souhaitez gérer plusieurs cas de type T :

 func myFunc(s: MyStruct) -> T? { switch s { case let sType as MyProtocol: // do MyProtocol specific stuff here, using sType default: //this does not conform to MyProtocol ... } } 

Vous devez déclarer le protocole comme @objc :

 @objc protocol MyProtocol { ... } 

Du livre “The Swift Programming Language” d’Apple:

Vous pouvez vérifier la conformité du protocole uniquement si votre protocole est marqué avec l’atsortingbut @objc, comme indiqué pour le protocole HasArea ci-dessus. Cet atsortingbut indique que le protocole doit être exposé au code Objective-C et est décrit dans Utilisation de Swift avec Cocoa et Objective-C. Même si vous n’interagissez pas avec Objective-C, vous devez marquer vos protocoles avec l’atsortingbut @objc si vous souhaitez pouvoir vérifier la conformité du protocole.

Notez également que les protocoles @objc ne peuvent être adoptés que par les classes et non par des structures ou des énumérations. Si vous marquez votre protocole comme @objc afin de vérifier la conformité, vous ne pourrez appliquer ce protocole qu’aux types de classe.