English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Swift fornisce generics per scrivere funzioni e tipi flessibili e riutilizzabili.
La libreria standard di Swift è costruita attraverso il codice generics.
Gli array e le dizionari di Swift sono set di generics.
Puoi creare un array di Int, un array di String, o persino un array di qualsiasi altro tipo di dati di Swift.
Esempio di funzione non generics chiamata exchange per scambiare due valori Int:
// Definire una funzione per scambiare due variabili func swapTwoInts(_ a: inout Int, _ b: inout Int) { let temporaryA = a a = b b = temporaryA } var numb1 = 100 var numb2 = 200 print("Dati prima dello scambio: \(numb1) e \(numb2)") swapTwoInts(&numb1, &numb2) print("Dati dopo lo scambio: \(numb1) e \(numb2)")
L'output dell'esecuzione del programma sopra è:
Dati prima dello scambio: 100 e 200 Dati dopo lo scambio: 200 e 100
L'esempio sopra è solo per scambiare variabili di tipo Int. Se si desidera scambiare due valori String o Double, è necessario scrivere una funzione corrispondente, ad esempio swapTwoStrings(_:_:) e swapTwoDoubles(_:_:), come segue:
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 }
Dalla visione del codice sopra, le loro funzioni di codice sono identiche, ma diversi per tipo, in questo caso possiamo utilizzare il generics per evitare di scrivere codice duplicato.
L'uso di tipi di nome sostitutivo (qui rappresentato con la lettera T) nel generics sostituisce i nomi di tipo effettivi (ad esempio Int, String o Double).
func swapTwoValues<T>(_ a: inout T, _ b: inout T)
Dopo swapTwoValues segue il nome placeholder del tipo (T), racchiuso tra parentesi tonde (<T>
)。Questo apice dice a Swift che T è un nome placeholder del tipo all'interno della definizione della funzione swapTwoValues(_:_:), quindi Swift non cerca un tipo reale chiamato T.
Esempio di funzione generica exchange per scambiare valori di tipo Int e String:
// Definire una funzione per scambiare due variabili func swapTwoValues<T>(_ a: inout T, _ b: inout T) { let temporaryA = a a = b b = temporaryA } var numb1 = 100 var numb2 = 200 print("Dati prima dello scambio: \(numb1) e \(numb2)") swapTwoValues(&numb1, &numb2) print("Dati dopo lo scambio: \(numb1) e \(numb2)") var str1 = "A" var str2 = "B" print("Dati prima dello scambio: \(str1) e \(str2)") swapTwoValues(&str1, &str2) print("Dati dopo lo scambio: \(str1) e \(str2)")
L'output dell'esecuzione del programma sopra è:
Dati prima dello scambio: 100 e 200 Dati dopo lo scambio: 200 e 100 Dati prima dello scambio: A e B Dati dopo lo scambio: B e A
Swift ti permette di definire i tuoi tipi generici.
Le classi personalizzate, le strutture e gli enumeri si applicano a qualsiasi tipo, come l'uso di Array e Dictionary.
Procediamo a scrivere un tipo di集合 generico chiamato Stack (stack), che permette di aggiungere nuovi elementi solo all'estremità del set (chiamato push), e può anche rimuovere elementi solo dall'estremità (chiamato pop).
L'analisi dalla sinistra alla destra dell'immagine è la seguente:
Ci sono tre valori nello stack.
Il quarto valore è stato spostato in cima allo stack.
Ora ci sono quattro valori nello stack, il valore più recente inserito è in alto.
Il valore più in alto dello stack viene rimosso, o chiamato pop.
Dopo aver rimosso un valore, ora lo stack ha solo tre valori.
Esempio di versione non generica di stack, prenda Stack di tipo Int come esempio:
struct IntStack {}} var items = [Int]() mutating func push(_ item: Int) { items.append(item) } mutating func pop() -> Int { return items.removeLast() } }
Questa struttura utilizza un attributo Array chiamato items per memorizzare valori. Stack fornisce due metodi: push(_) e pop(), per inserire valori nello stack e rimuoverli dallo stack. Questi metodi sono marcati come mutating, perché devono modificare l'array items della struttura.
La struttura IntStack sopra menzionata può essere utilizzata solo per il tipo Int. Tuttavia, è possibile definire una struttura Stack generica che possa gestire valori di qualsiasi tipo.
Di seguito è riportata la versione generica dello stesso codice:
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("Elemento di stringa inserito nella coda: ") stackOfStrings.push("google") stackOfStrings.push("w3codebox") print(stackOfStrings.items); let deletetos = stackOfStrings.pop() print("Elemento uscente: "+ deletetos) var stackOfInts = Stack<Int>() print("Inserimento di elementi interi: ") stackOfInts.push(1) stackOfInts.push(2) print(stackOfInts.items);
Il risultato dell'esecuzione dell'esempio è:
Inserimento di un elemento di stringa nella coda: ["google", "w3codebox"] Elemento uscente: w3codebox Inserimento di elementi interi nello stack: [1, 2]
Stack è基本上 e IntStack identici, il parametro di tipo segnaposto Element sostituisce il tipo Int reale.
Nell'esempio sopra, Element viene utilizzato come segnaposto in tre luoghi:
Creare items Proprietà, utilizzando Element Un array vuoto dello stesso tipo per inizializzarlo.
Specificato push(_:) Il parametro unico del metodo item Il tipo deve essere Element Tipo.
Specificato pop() Il tipo di ritorno del metodo deve essere Element Tipo.
Quando si estende un tipo generico (usando la parola chiave extension), non è necessario fornire una lista di parametri di tipo nella definizione dell'estensione. È più comodo, perché la lista di parametri di tipo dichiarata nella definizione originale del tipo può essere utilizzata nell'estensione, e i nomi dei parametri provenienti dal tipo originale vengono utilizzati come riferimenti ai parametri di tipo nella definizione originale.
Esempio di estensione del tipo generico Stack, che aggiunge una proprietà calcolata readonly chiamata topItem, che restituisce l'elemento in cima allo stack senza rimuoverlo:
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("Elemento di stringa inserito nella coda: ") stackOfStrings.push("google") stackOfStrings.push("w3codebox") if let topItem = stackOfStrings.topItem { print("L'elemento in cima allo stack è: \(topItem).") } print(stackOfStrings.items)
Nell'esempio, l'attributo topItem restituirà un valore opzionale di tipo Element. Quando lo stack è vuoto, topItem restituirà nil; quando lo stack non è vuoto, topItem restituirà l'ultimo elemento dell'array items.
L'output dell'esecuzione del programma sopra è:
Inserimento di un elemento di stringa nella coda: L'elemento in cima allo stack è: w3codebox. ["google", "w3codebox"]
Possiamo anche specificare un tipo correlato attraverso l'estensione di un tipo esistente.
Ad esempio, il tipo Array di Swift offre già il metodo append(_:], un'attributo count e un'indice di accesso che accetta un valore di tipo Int per recuperare i suoi elementi. Queste tre funzionalità soddisfano i requisiti del protocollo Container, quindi puoi semplicemente dichiarare che Array adotta il protocollo per estenderlo.
Esempio di creazione di un'estensione vuota:
extension Array: Container {}
Le restrizioni di tipo specificano un tipo parametrico che deve essere derivato da una classe specificata o che deve seguire un protocollo o una composizione di protocolli.
Puoi scrivere una restrizione di tipo dopo un nome di tipo parametrico, separata da due punti, come parte di una catena di parametri di tipo. La sintassi di base per le restrizioni di tipo che agiscono sulle funzioni generiche è come segue (uguale alla sintassi dei tipi generici):
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) { // Questa è la parte del corpo della funzione generica }
Questa funzione ha due parametri di tipo. Il primo parametro di tipo T, ha la restrizione che T deve essere un tipo derivato da SomeClass; il secondo parametro di tipo U, ha la restrizione che U deve conformare al tipo di contratto SomeProtocol.
// Funzione non generica, ricerca dell'indice della stringa specificata nell'array func findIndex(ofString valueToFind: String, in array: [String]) -> Int? { for (index, value) in array.enumerated() { if value == valueToFind { // Trovato, restituisce il valore dell'indice return index } } return nil } let strings = ["google", "weibo", "taobao", "w3codebox", "facebook"] if let foundIndex = findIndex(ofString: "w3codebox", in: strings) { print("L'indice di w3codebox è \(foundIndex)") }
L'indice dell'indice inizia da 0.
L'output dell'esecuzione del programma sopra è:
L'indice di w3codebox è 3
In Swift, l'uso della parola chiave associatedtype per impostare l'esempio di tipo correlato.
Di seguito viene definito un accordo Container, che definisce un tipo correlato ItemType.
L'accordo Container specifica solo tre funzionalità che devono essere fornite da qualsiasi tipo che segue l'accordo Container. I tipi che seguono l'accordo possono anche fornire funzionalità aggiuntive se soddisfano queste tre condizioni.
// protocollo Container protocol Container { associatedtype ItemType // aggiungi un nuovo elemento al contenitore mutating func append(_ item: ItemType) // ottieni il numero di elementi nel contenitore var count: Int { get } // recupera ogni elemento del contenitore tramite un valore di indice di tipo Int subscript(i: Int) -> ItemType { get } } // La struttura Stack segue l'accordo Container struct Stack<Element>: Container { // parte originale dell'implementazione di Stack<Element> var items = [Element]() mutating func push(_ item: Element) { items.append(item) } mutating func pop() -> Element { return items.removeLast() } // parte dell'implementazione del protocollo 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") // Elenco degli elementi print(tos.items) // Numero di elementi print( tos.count)
L'output dell'esecuzione del programma sopra è:
["google", "w3codebox", "taobao"] 3
Le restrizioni di tipo assicurano che il tipo soddisfi le restrizioni di definizione della funzione generica o della classe.
Puoi definire le restrizioni dei parametri attraverso una frase where nel elenco dei parametri.
Puoi scrivere una frase where, seguita dalla lista dei parametri di tipo, dove la frase where può avere una o più restrizioni sui tipi correlati, nonché (o) una o più relazioni di uguaglianza tra tipi e tipi correlati.
Esempio seguente definisce una funzione generica chiamata allItemsMatch, utilizzata per verificare se due esempi di Container contengono gli stessi elementi nell'ordine stesso.
Se tutti gli elementi possono essere abbinati, restituisce true, altrimenti restituisce false.
// protocollo Container protocol Container { associatedtype ItemType // aggiungi un nuovo elemento al contenitore mutating func append(_ item: ItemType) // ottieni il numero di elementi nel contenitore var count: Int { get } // recupera ogni elemento del contenitore tramite un valore di indice di tipo Int subscript(i: Int) -> ItemType { get } } // // tipo generico TOS che segue il protocollo Container struct Stack<Element>: Container { // parte originale dell'implementazione di Stack<Element> var items = [Element]() mutating func push(_ item: Element) { items.append(item) } mutating func pop() -> Element { return items.removeLast() } // parte dell'implementazione del protocollo Container mutating func append(_ item: Element) { self.push(item) } var count: Int { return items.count } subscript(i: Int) -> Element { return items[i] } } // estensione, utilizza Array come Container extension Array: Container {} func allItemsMatch<C1: Container, C2: Container>() (_ someContainer: C1, _ anotherContainer: C2) -> Bool where C1.ItemType == C2.ItemType, C1.ItemType: Equatable { // verifica se due contenitori contengono lo stesso numero di elementi se someContainer.count != anotherContainer.count { return false } // Verifica che ogni coppia di elementi sia uguale for i in 0..<someContainer.count { if someContainer[i] != anotherContainer[i] { return false } } // Tutti gli elementi corrispondono, restituisce true return true } var tos = Stack<String>() tos.push("google") tos.push("w3codebox") tos.push("taobao") var aos = ["google", "w3codebox", "taobao"] if allItemsMatch(tos, aos) { print("Abbinamento di tutti gli elementi") } print("Elemento non corrispondente") }
L'output dell'esecuzione del programma sopra è:
Abbinamento di tutti gli elementi