Mehrere Typbeschränkungen in Swift

133

Angenommen, ich habe diese Protokolle:

protocol SomeProtocol {

}

protocol SomeOtherProtocol {

}

Wenn ich nun eine Funktion möchte, die einen generischen Typ annimmt, aber diesem Typ entsprechen muss, kann SomeProtocolich Folgendes tun:

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

Aber gibt es eine Möglichkeit, eine Typbeschränkung für mehrere Protokolle hinzuzufügen?

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

}

Ähnliche Dinge verwenden Kommas, aber in diesem Fall würde die Deklaration eines anderen Typs gestartet. Folgendes habe ich versucht.

<T: SomeProtocol | SomeOtherProtocol>
<T: SomeProtocol , SomeOtherProtocol>
<T: SomeProtocol : SomeOtherProtocol>
Logan
quelle
Dies ist eine besonders relevante Frage, da die Swift-Dokumente dies im Generika-Kapitel nicht erwähnen ...
Bruno Philipe

Antworten:

241

Sie können eine where-Klausel verwenden, mit der Sie beliebig viele Anforderungen angeben können (alle müssen erfüllt sein), die durch Kommas getrennt sind

Swift 2:

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

Swift 3 & 4:

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

oder die mächtigere where-Klausel:

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

Sie können natürlich die Protokollzusammensetzung (z. B. protocol<SomeProtocol, SomeOtherProtocol>) verwenden, diese ist jedoch etwas weniger flexibel.

Mit wherekönnen Sie Fälle behandeln, in denen mehrere Typen beteiligt sind.

Möglicherweise möchten Sie weiterhin Protokolle zur Wiederverwendung an mehreren Stellen erstellen oder dem zusammengestellten Protokoll nur einen aussagekräftigen Namen geben.

Swift 5:

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

Dies fühlt sich natürlicher an, da die Protokolle neben dem Argument stehen.

Jiaaro
quelle
Meine Güte, das ist nicht logisch, aber gut zu wissen, dass ich nur einer der Dankes-Spammer für diesen sein möchte. Ich habe das in einem Monat nicht bemerkt, seit ich es brauchte.
Mathijs Segers
3
Gibt es eine Möglichkeit, dasselbe mit Klassen und Strukturen im Typ Contraint-Ausdruck zu tun? zB <T where T:SomeStruct, T:AnotherStruct>? Für Klassen scheint der Compiler dies so zu interpretieren, dass er sagt "T ist eine Unterklasse von beiden", und für Strukturen beschwert er sich nur darüber "Type 'T' constrained to non-protocol type".
Jarrod Smith
Für das spezifische Beispiel in der Frage des OP: s sollte die Zusammensetzung des Fragenprotokolls eine vorzuziehende Methode sein: Die obige Lösung ist gültig, aber imho, überlastet die Funktionssignatur unnötig. Wenn Sie die Protokollzusammensetzung beispielsweise als Typeinschränkung verwenden , können Sie die whereKlausel weiterhin für zusätzliche Typ- / andere Verwendung verwenden, z. B. func someFunc<U, T: protocol<SomeProtocol, SomeOtherProtocol> where T.SubType == U>(arg: T, arg2: U) { ... }für Typealien SubTypein z SomeProtocol.
dfri
1
Sieht so aus, als wäre dies in swift3 veraltet und empfiehlt die Verwendung von: func someFunc <T> (arg: T) wobei T: SomeProtocol, T: SomeOtherProtocol {
Cristi Băluță
2
Gibt es eine Möglichkeit, schnell zu erkennen, dass T von einem bestimmten Objekttyp sein UND ein bestimmtes Protokoll implementieren muss?
Georg
73

Sie haben zwei Möglichkeiten:

  1. Sie verwenden eine where-Klausel, wie in Jiaaros Antwort angegeben:

    func someFunc<T where T : SomeProtocol, T : SomeOtherProtocol>(arg: T) {
        // do stuff
    }
    
  2. Sie verwenden einen Protokollzusammensetzungstyp :

    func someFunc<T : protocol<SomeProtocol, SomeOtherProtocol>>(arg: T) {
        // do stuff
    }
    
Jean-Philippe Pellet
quelle
2
Imo die zweite Lösung ist schöner, ich würde für diese Antwort gehen, es ist auch vollständiger zwei Optionen präsentieren
Mathijs Segers
2
Nummer 2 ist die einzige, die unter Swift 2 für mich funktioniert, wenn ich a deklariere typealias. Vielen Dank!
Bruno Philipe
19

Die Entwicklung zu Swift 3.0 bringt einige Änderungen mit sich. Unsere beiden Möglichkeiten sehen jetzt etwas anders aus.

Verwenden einer whereKlausel in Swift 3.0:

Die whereKlausel wurde jetzt an das Ende einer Funktionssignatur verschoben, um die Lesbarkeit zu verbessern. Die Vererbung mehrerer Protokolle sieht nun folgendermaßen aus:

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

}

Verwenden des protocol<>Konstrukts in Swift 3.0:

Die Zusammensetzung unter Verwendung des protocol<>Konstrukts ist veraltet. Das frühere protocol<SomeProtocol, SomeOtherProtocol>sieht jetzt so aus:

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

}

Verweise.

Weitere Informationen zu den Änderungen für wherefinden Sie hier: https://github.com/apple/swift-evolution/blob/master/proposals/0081-move-where-expression.md

Weitere Informationen zu den Änderungen für das Protokoll <> -Konstrukt finden Sie hier: https://github.com/apple/swift-evolution/blob/master/proposals/0095-any-as-existential.md

ncke
quelle
13

Swift 3 bietet bis zu 3 verschiedene Möglichkeiten, Ihre Funktion zu deklarieren.

protocol SomeProtocol {
    /* ... */
}

protocol SomeOtherProtocol {
    /* ... */        
}

1. &Operator verwenden

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

2. whereKlausel verwenden

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

3. whereKlausel und &Operator verwenden

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

Beachten Sie auch, dass Sie verwenden können typealias, um Ihre Funktionsdeklaration zu verkürzen.

typealias RequiredProtocols = SomeProtocol & SomeOtherProtocol

func someFunc<T: RequiredProtocols>(arg: T) {
    /* ... */   
}
Imanou Petit
quelle