Vincoli di tipo multiplo in Swift


133

Diciamo che ho questi protocolli:

protocol SomeProtocol {

}

protocol SomeOtherProtocol {

}

Ora, se voglio una funzione che accetta un tipo generico, ma a quel tipo deve essere conforme, SomeProtocolpotrei fare:

func someFunc<T: SomeProtocol>(arg: T) {
    // do stuff
}

Ma c'è un modo per aggiungere un vincolo di tipo per più protocolli?

func bothFunc<T: SomeProtocol | SomeOtherProtocol>(arg: T) {

}

Cose simili usano le virgole, ma in questo caso, inizierebbe la dichiarazione di un tipo diverso. Ecco cosa ho provato.

<T: SomeProtocol | SomeOtherProtocol>
<T: SomeProtocol , SomeOtherProtocol>
<T: SomeProtocol : SomeOtherProtocol>

Questa è una domanda particolarmente rilevante in quanto i documenti di Swift non ne fanno menzione nel capitolo sui generici ...
Bruno Philipe,

Risposte:


241

È possibile utilizzare una clausola where che consente di specificare tutti i requisiti desiderati (che devono essere soddisfatti tutti) separati da virgole

Swift 2:

func someFunc<T where T:SomeProtocol, T:SomeOtherProtocol>(arg: T) {
    // stuff
}

Swift 3 e 4:

func someFunc<T: SomeProtocol & SomeOtherProtocol>(arg: T) {
    // stuff
}

o la clausola where più potente:

func someFunc<T>(arg: T) where T:SomeProtocol, T:SomeOtherProtocol{
    // stuff
}

Ovviamente puoi usare la composizione del protocollo (ad es. protocol<SomeProtocol, SomeOtherProtocol>), Ma è un po 'meno flessibile.

L'utilizzo whereconsente di gestire i casi in cui sono coinvolti più tipi.

Potresti comunque voler comporre protocolli per il riutilizzo in più posizioni o semplicemente dare un nome significativo al protocollo composto.

Swift 5:

func someFunc(arg: SomeProtocol & SomeOtherProtocol) { 
    // stuff
}

Ciò sembra più naturale poiché i protocolli sono accanto all'argomento.


Accidenti, non è logico, ma è bene sapere che voglio solo essere uno degli spammer di ringraziamento per questo, non me ne sono reso conto in un mese da quando ne avevo bisogno.
Mathijs Segers,

3
Qualche modo di fare la stessa cosa con le classi e le strutture nell'espressione del contrappunto di tipo? ad es <T where T:SomeStruct, T:AnotherStruct>. Per le classi, il compilatore sembra interpretarlo dicendo che "T è una sottoclasse di entrambi", e per le strutture si lamenta solo di ciò "Type 'T' constrained to non-protocol type".
Jarrod Smith,

Per l'esempio specifico nella composizione del protocollo di domanda dell'OP: s dovrebbe essere il metodo preferibile: la soluzione sopra è valida, ma, imho, ingombra inutilmente la firma della funzione. Inoltre, l'utilizzo della composizione del protocollo come, ad esempio, un vincolo di tipo, consente comunque di utilizzare la whereclausola per un tipo aggiuntivo / altro utilizzo, ad es . func someFunc<U, T: protocol<SomeProtocol, SomeOtherProtocol> where T.SubType == U>(arg: T, arg2: U) { ... }Per typealias SubTypein es SomeProtocol.
venerdì

1
Sembra che questo sia deprecato in swift3 e raccomanda di usare: func someFunc <T> (arg: T) dove T: SomeProtocol, T: SomeOtherProtocol {
Cristi Băluță

2
C'è un modo per dire rapidamente che T deve essere di un certo tipo di oggetto E implementare un certo protocollo?
Georg,

73

Hai due possibilità:

  1. Usi una clausola where come indicato nella risposta di Jiaaro:

    func someFunc<T where T : SomeProtocol, T : SomeOtherProtocol>(arg: T) {
        // do stuff
    }
    
  2. Si utilizza un tipo di composizione protocollo :

    func someFunc<T : protocol<SomeProtocol, SomeOtherProtocol>>(arg: T) {
        // do stuff
    }
    

2
imo la seconda soluzione è più carina, vorrei questa risposta è anche più completa presentando due opzioni
Mathijs Segers,

2
Il numero 2 è l'unico che funziona per me in Swift 2 quando dichiari a typealias. Grazie!
Bruno Philipe,

19

L'evoluzione in Swift 3.0 porta alcuni cambiamenti. Le nostre due scelte ora sembrano leggermente diverse.

Utilizzando una whereclausola in Swift 3.0:

La whereclausola è ora spostata alla fine della firma di una funzione per migliorare la leggibilità. Quindi l'ereditarietà di più protocolli ora appare così:

func someFunc<T>(arg: T) where T:SomeProtocol, T:SomeOtherProtocol {

}

Utilizzando il protocol<>costrutto in Swift 3.0:

La composizione che utilizza il protocol<>costrutto è obsoleta. Il precedente protocol<SomeProtocol, SomeOtherProtocol>ora appare così:

func someFunc<T:SomeProtocol & SomeOtherProtocol>(arg: T) {

}

Riferimenti.

Maggiori informazioni sulle modifiche per wheresono disponibili qui: https://github.com/apple/swift-evolution/blob/master/proposals/0081-move-where-expression.md

E ulteriori informazioni sulle modifiche per il protocollo <> sono disponibili qui: https://github.com/apple/swift-evolution/blob/master/proposals/0095-any-as-existential.md


13

Swift 3 offre fino a 3 modi diversi per dichiarare la tua funzione.

protocol SomeProtocol {
    /* ... */
}

protocol SomeOtherProtocol {
    /* ... */        
}

1. Utilizzo &dell'operatore

func someFunc<T: SomeProtocol & SomeOtherProtocol>(arg: T) {
    /* ... */
}

2. Utilizzo della whereclausola

func someFunc<T>(arg: T) where T: SomeProtocol, T: SomeOtherProtocol {
    /* ... */
}

3. Utilizzo della whereclausola e &dell'operatore

func someFunc<T>(arg: T) where T: SomeProtocol & SomeOtherProtocol {
    /* ... */        
}

Si noti inoltre che è possibile utilizzare typealiasper abbreviare la dichiarazione delle funzioni.

typealias RequiredProtocols = SomeProtocol & SomeOtherProtocol

func someFunc<T: RequiredProtocols>(arg: T) {
    /* ... */   
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.