2016-05-25 3 views
5

Ich möchte die ständige Notwendigkeit vereinfachenBenutzerdefinierte Bediener zu vereinfachen If-Let

if let firstName = firstName { 
    self.name = firstName 
} 

Eine mögliche individuelle, generische Betreiber zu tun dies zu tun wäre,

infix operator ?= {} 
func ?= <T>(inout left: T, right: T?) { 
    if let right = right { 
     left = right 
    } 
} 

das vorherige Beispiel zu vereinfachen zu

self.name ?= firstName 

Dies schafft ein Problem, wenn vorName 's Wert ist Null, dann Swift wird den Wert in einem optionalen umbrechen.

var name: String? = "Bob" 
var firstName: String? = nil 

self.name ?= firstName 

print(self.name) 
/* 
    prints "nil" since it is wrapping firstName in an optional when 
    passed in.  

    E.g. Optional<nil> gets unwrapped to nil in function and assigned 
*/ 

Eine mögliche Lösung für den benutzerdefinierten Operator? Ich habe versucht, den linken Parameter so zu beschränken, dass er nicht optional ist, aber das ist mit generischen Typbeschränkungen nicht möglich.

Antwort

10

Das Problem (wie Sie richtig erkannt haben) ist, dass, weil das Argument der linken Seite vom Typ T ist, wenn Sie eine optionale in es T übergeben werden, wird als Optional<Whatever> abgeleitet werden. Da das Argument auf der rechten Seite T? ist (und weil Typen frei zu Optionals hochgestuft werden können), wird der Typ auf Optional<Optional<Whatever>> zurückgeführt, was zu der verwirrenden doppelten Umhüllung führt, die Sie beobachten.

Die Lösung besteht darin, eine Überladung hinzuzufügen, um mit der Situation umzugehen, wenn das Argument auf der linken Seite ebenfalls optional ist.

infix operator ?= {} 
func ?= <T>(inout left: T, right: T?) { 
    if let right = right { 
     left = right 
    } 
} 

// overload to deal with an optional left handed side 
func ?= <T>(inout left: T?, right: T?) { 
    if let right = right { 
     left = right 
    } 
} 

(Beachten Sie, dass in Swift 3, inout vor dem Parametertyp angezeigt werden soll)

Nun, wenn Sie diesen Operator mit einem optionalen als linkshändige Argument verwenden, wird Swift die überladene Version verwenden, anstatt von der ursprünglichen Version, da es immer die mehr typspezifische Unterschrift bevorzugt. Dies bedeutet, dass die rechte Seite nicht in ein doppeltes optionales Format eingeschlossen wird, da es nun vom genau gleichen Typ wie das Argument auf der linken Seite ist.

var name: String? = "Bob" 
var firstName: String? = nil 

name ?= firstName 

print(name) // prints: Optional("Bob") 

Beachten Sie, dass dies ähnlich ist, was die ?? tut, hat es zwei Definitionen, um mit einer Seite optional sein, eine Seite ist nicht optional & beide Seiten optional sein, um die Erzeugung von Doppel zu vermeiden gewickelten Extras:

@warn_unused_result 
public func ??<T>(optional: T?, @autoclosure defaultValue:() throws -> T) rethrows -> T 

@warn_unused_result 
public func ??<T>(optional: T?, @autoclosure defaultValue:() throws -> T?) rethrows -> T? 
+2

Das ist ziemlich cool – marchinram

0

fand ich eine interessante Möglichkeit, den Aufwand für if let von Nil Coalescing Operator

zu reduzieren

Der Nullkoaleszenzoperator (a ?? b) entpackt ein optionales a, wenn es einen Wert enthält oder einen Standardwert b zurückgibt, wenn a Null ist. Der Ausdruck a ist immer ein optionaler Typ. Der Ausdruck b muss mit dem Typ übereinstimmen, der in einem gespeichert ist.

Kurz - nonOptional = optional ?? someDefaultValue
Z.B.-

let defaultColorName = "red" 
var userDefinedColorName: String? // optional variable 

var colorNameToUse = userDefinedColorName ?? defaultColorName 

Also, hier, wenn userDefinedColorName null ist, dann wird defaultColorName zu nicht-optionale Variable colorNameToUse assighed erhalten werden.
Für eine vollständige Referenzprüfung dieses Swift documentation