English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Swift fournit des generics pour écrire des fonctions et des types flexibles et réutilisables.
La bibliothèque standard de Swift est construite en utilisant du code générique.
Les types d'arrays et de dictionnaires de Swift sont des ensembles génériques.
Vous pouvez créer un tableau Int, un tableau String, ou même un tableau de données de type Swift.
Le prochain exemple est une fonction non générique exchange utilisée pour échanger deux valeurs Int :
// Définir une fonction d'échange de deux variables func swapTwoInts(_ a: inout Int, _ b: inout Int) { let temporaryA = a a = b b = temporaryA } var numb1 = 100 var numb2 = 200 print("Données avant l'échange: \(numb1) et \(numb2)") swapTwoInts(&numb1, \&numb2) print("Données après l'échange: \(numb1) et \(numb2)")
Le résultat de l'exécution du programme ci-dessus est :
Données avant l'échange: 100 et 200 Données après l'échange: 200 et 100
Les exemples ci-dessus ne s'appliquent qu'à l'échange de variables de type Int. Si vous souhaitez échanger deux valeurs String ou Double, vous devez écrire une fonction correspondante, par exemple swapTwoStrings(_:_:) et swapTwoDoubles(_:_:), comme suit :
func swapTwoStrings(_ a: inout String, _ b: inout String) { let temporaryA = a a = b b = temporaryA } func swapTwoDoubles(_ a: inout Double, _ b: inout Double) { let temporaryA = a a = b b = temporaryA }
D'après le code ci-dessus, leur code fonctionnel est le même, mais leurs types sont différents. Dans ce cas, nous pouvons utiliser des types génériques pour éviter de réécrire le code.
Les types génériques utilisent des noms de types de remplacement (ici représentés par la lettre T) pour remplacer les noms de types réels (par exemple Int, String ou Double).
func swapTwoValues<T>(_ a: inout T, _ b: inout T)
swapTwoValues est suivi du nom de type de remplacement (ici utilisé T) et est encadré par des crochets (<T>
)。Ces crochets aiguilles indiquent à Swift que T est un nom de type de remplacement à l'intérieur de la définition de la fonction swapTwoValues(_:_:), donc Swift ne cherche pas un type réel nommé T.
Le siguiente ejemplo es una función genérica exchange utilizada para intercambiar valores de Int y String:
// Définir une fonction d'échange de deux variables func swapTwoValues<T>(_ a: inout T, _ b: inout T) { let temporaryA = a a = b b = temporaryA } var numb1 = 100 var numb2 = 200 print("Données avant l'échange: \(numb1) et \(numb2)") swapTwoValues(&numb1, \&numb2) print("Données après l'échange: \(numb1) et \(numb2)") var str1 = \ var str2 = \ print("Données avant l'échange: \(str1) et \(str2)") swapTwoValues(&str1, \&str2) print("Données après l'échange: \(str1) et \(str2)")
Le résultat de l'exécution du programme ci-dessus est :
Données avant l'échange: 100 et 200 Données après l'échange: 200 et 100 Données avant l'échange: A et B Données après l'échange: B et A
Swift vous permet de définir vos propres types génériques.
Les classes personnalisées, les structures et les ensembles personnalisés s'appliquent à tous les types, comme les utilisations de Array et Dictionary.
Ensuite, nous allons écrire un type de collection générique nommé Stack (pile), qui ne permet que d'ajouter de nouveaux éléments à l'extrémité de la collection (ce qui s'appelle empiler), et ne peut que supprimer des éléments de l'extrémité (ce qui s'appelle dépiler).
L'interprétation de la gauche à la droite de l'image est la suivante :
Il y a trois valeurs dans la pile.
La quatrième valeur est ajoutée au sommet de la pile.
Il y a maintenant quatre valeurs dans la pile, la valeur ajoutée récemment est au sommet.
La valeur la plus au sommet de la pile est supprimée, ou appelée sortie de pile.
Après avoir supprimé une valeur, la pile ne contient maintenant que trois valeurs.
Voici un exemple d'une version non générique de pile, en utilisant une pile de type Int comme exemple :
struct IntStack { var items = [Int]() mutating func push(_ item: Int) { items.append(item) } mutating func pop() -> Int { return items.removeLast() } }
Cette structure utilise une propriété Array nommée items pour stocker les valeurs dans la pile. La Stack fournit deux méthodes : push(_) et pop(), pour ajouter des valeurs à la pile et les supprimer de la pile. Ces méthodes sont marquées comme mutating, car elles nécessitent de modifier le tableau items de la structure.
La structure IntStack ci-dessus ne peut être utilisée qu'avec le type Int. Cependant, vous pouvez définir une structure Stack générique pour pouvoir gérer n'importe quel type de valeur.
Voici la version générique du même code :
struct Stack<Element> { var items = [Element]() mutating func push(_ item: Element) { items.append(item) } mutating func pop() -> Element { return items.removeLast() } } var stackOfStrings = Stack<String>() print("Ajout d'un élément de chaîne dans la pile : ") stackOfStrings.push("google") stackOfStrings.push("w3codebox) print(stackOfStrings.items); let deletetos = stackOfStrings.pop() print("Élément sorti: " + deletetos) var stackOfInts = Stack<Int>() print("Ajouter un élément entier à la pile: ") stackOfInts.push(1) stackOfInts.push(2) print(stackOfInts.items);
Résultat d'exécution de l'exemple :
Ajout d'un élément de chaîne dans la pile : ["google", "w3codebox"] Élément sorti : w3codebox Ajouter un élément entier à la pile: [1, 2]
La Stack est presque identique à IntStack, le paramètre de type de remplacement Element remplace le type Int réel.
Dans l'exemple ci-dessus, Element est utilisé comme substitut dans trois endroits :
Créer items Propriété, utiliser Element Initialiser un tableau vide de ce type.
Spécifié push(_:) Le paramètre unique de la méthode item Le type doit être Element Type.
Spécifié pop() Le type de retour de la méthode doit être Element Type.
Lorsque vous étendez un type générique (en utilisant le mot-clé extension), vous n'avez pas besoin de fournir une liste de paramètres de type dans la définition de l'extension. Plus pratique encore, la liste des paramètres de type déclarés dans la définition originale du type peut être utilisée dans l'extension, et les noms des paramètres provenant du type original seront utilisés comme références aux paramètres de type dans la définition originale.
L'exemple suivant étend le type générique Stack, ajoutant une propriété calculée en lecture nommée topItem, qui retournera l'élément en haut de la pile sans le supprimer de la pile :}}
struct Stack<Element> { var items = [Element]() mutating func push(_ item: Element) { items.append(item) } mutating func pop() -> Element { return items.removeLast() } } extension Stack { var topItem: Element? { return items.isEmpty ? nil : items[items.count - 1] } } var stackOfStrings = Stack<String>() print("Ajout d'un élément de chaîne dans la pile : ") stackOfStrings.push("google") stackOfStrings.push("w3codebox) if let topItem = stackOfStrings.topItem { print("L'élément en haut de la pile est : \(topItem).") } print(stackOfStrings.items)
Dans cet exemple, la propriété topItem retourne une valeur optionnelle de type Element. Lorsque la pile est vide, topItem retourne nil ; lorsque la pile n'est pas vide, topItem retourne l'élément de la dernière position de l'array items.
Le résultat de l'exécution du programme ci-dessus est :
Ajout d'un élément de chaîne dans la pile : L'élément en haut de la pile est : w3codebox. ["google", "w3codebox"]
Nous pouvons également spécifier des types associés en étendant un type existant.
Par exemple, le type Array de Swift fournit déjà la méthode append(_:], une propriété count, et un index sous forme d'Int pour récupérer ses éléments. Ces trois fonctionnalités répondent aux exigences du protocole Container, donc vous pouvez simplement déclarer que Array adopte ce protocole pour étendre Array.
Le suivant est un exemple de création d'une extension vide :
extension Array: Container {}
Les contraintes de type spécifient un type de paramètre qui doit hériter d'une classe spécifiée ou suivre une协议 ou une composition de protocoles spécifiques.
Vous pouvez écrire une contrainte de type après un nom de paramètre de type, séparée par une virgule, en tant que partie de la chaîne de paramètres de type. La syntaxe de base des contraintes de type agissant sur les fonctions génériques, comme pour les types génériques, est présentée ci-dessous :
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) { // Voici la partie du corps de la fonction générique. }
Cette fonction a deux paramètres de type. Le premier paramètre T, qui impose que T soit un sous-classe de SomeClass; le second paramètre U, qui impose que U respecte la convention SomeProtocol.
// Fonction non générique, recherche de l'indice d'une chaîne spécifique dans un tableau func findIndex(ofString valueToFind: String, in array: [String]) -> Int? { for (index, value) in array.enumerated() { if value == valueToFind { // Retourner l'indice trouvé return index } } return nil } let strings = ["google", "weibo", "taobao", "w3codebox", "facebook"] if let foundIndex = findIndex(ofString: "w3codebox", in: strings) { print("w3L'index de codebox est (foundIndex)") }
Les indices des sous-indices commencent à 0.
Le résultat de l'exécution du programme ci-dessus est :
w3L'index de codebox est 3
Swift utilise le mot-clé associatedtype pour définir des types associés en exemple.
L'exemple suivant définit une convention Container, qui définit un type associé ItemType.
La convention Container ne spécifie que trois fonctionnalités que doivent fournir tous les types qui obéissent à la convention Container. Les types qui obéissent à la convention peuvent également fournir d'autres fonctionnalités supplémentaires dans le cas où elles satisfont à ces trois conditions.
// protocole Container protocol Container { associatedtype ItemType // ajouter un nouvel élément dans le conteneur mutating func append(_ item: ItemType) // obtenir le nombre d'éléments du conteneur var count: Int { get } // récupérer chaque élément du conteneur par l'index de type Int subscript(i: Int) -> ItemType { get } } // La structure Stack obéit à la convention Container struct Stack<Element>: Container { // partie originale de l'implémentation de Stack<Element> var items = [Element]() mutating func push(_ item: Element) { items.append(item) } mutating func pop() -> Element { return items.removeLast() } // implémentation de la partie du protocole Container mutating func append(_ item: Element) { self.push(item) } var count: Int { return items.count } subscript(i: Int) -> Element { return items[i] } } var tos = Stack<String>() tos.push("google") tos.push("w3codebox) tos.push("taobao") // Liste d'éléments print(tos.items) // Nombre d'éléments print( tos.count )
Le résultat de l'exécution du programme ci-dessus est :
["google", "w3["codebox", "taobao"] 3
Les contraintes de type peuvent garantir que le type correspond aux contraintes de définition des fonctions ou des classes génériques.
Vous pouvez définir des contraintes sur les paramètres à l'aide d'une instruction where dans la liste des paramètres.
Vous pouvez écrire une instruction where, immédiatement après la liste des paramètres de type, suivie d'une ou plusieurs contraintes sur les types associés, ainsi que (ou) une ou plusieurs relations d'égalité (equality) entre les types et les types associés.
l'exemple suivant définit une fonction générique nommée allItemsMatch, utilisée pour vérifier si deux exemples de Container contiennent des éléments de même ordre et identiques.
si tous les éléments peuvent correspondre, retourne true, sinon retourne false.
// protocole Container protocol Container { associatedtype ItemType // ajouter un nouvel élément dans le conteneur mutating func append(_ item: ItemType) // obtenir le nombre d'éléments du conteneur var count: Int { get } // récupérer chaque élément du conteneur par l'index de type Int subscript(i: Int) -> ItemType { get } } // // type générique TOS suivant le protocole Container struct Stack<Element>: Container { // partie originale de l'implémentation de Stack<Element> var items = [Element]() mutating func push(_ item: Element) { items.append(item) } mutating func pop() -> Element { return items.removeLast() } // implémentation de la partie du protocole Container mutating func append(_ item: Element) { self.push(item) } var count: Int { return items.count } subscript(i: Int) -> Element { return items[i] } } // extension, utiliser Array comme Container extension Array: Container {} func allItemsMatch<C1: Container, C2: Container> (_ someContainer: C1, _ anotherContainer: C2) -> Bool where C1.ItemType == C2.ItemType, C1.ItemType: Equatable { // vérifier si deux conteneurs contiennent le même nombre d'éléments si someContainer.count != anotherContainer.count { return false } // Vérifiez si chaque paire d'éléments est égale for i in 0..<someContainer.count { if someContainer[i] != anotherContainer[i] { return false } } // Tous les éléments sont correspondants, retourne true return true } var tos = Stack<String>() tos.push("google") tos.push("w3codebox) tos.push("taobao") var aos = ["google", "w3["codebox", "taobao"] if allItemsMatch(tos, aos) { print("Concordance de tous les éléments") } print("Élément non correspondant") }
Le résultat de l'exécution du programme ci-dessus est :
Concordance de tous les éléments