Überprüfen des Werts eines optionalen Bool

86

Wenn ich überprüfen möchte, ob ein optionaler Bool wahr ist, funktioniert dies nicht:

var boolean : Bool? = false
if boolean{
}

Dies führt zu diesem Fehler:

Optionaler Typ '@IvalueBool?' kann nicht als Boolescher Wert verwendet werden; Teste stattdessen auf '! = nil'

Ich möchte nicht nach Null suchen. Ich möchte überprüfen, ob der zurückgegebene Wert wahr ist.

Muss ich immer tun, if boolean == truewenn ich mit einem optionalen Bool arbeite?

Sollte BooleanTypeder Compiler nicht wissen, dass ich den Wert des Bool überprüfen möchte, da Optionals nicht mehr den Anforderungen entsprechen?

Mondkatze
quelle
Da Boolesche Werte dem Equatable-Protokoll entsprechen, können Sie ein optionales mit einem nicht optionalen Protokoll vergleichen. Siehe hier
Honey

Antworten:

188

Bei optionalen Booleschen Werten muss die Prüfung explizit erfolgen:

if boolean == true {
    ...
}

Andernfalls können Sie das optionale auspacken:

if boolean! {
    ...
}

Aber das erzeugt eine Laufzeitausnahme, wenn boolean ist nil- um das zu verhindern:

if boolean != nil && boolean! {
    ...
}

Vor Beta 5 war dies möglich, aber es wurde geändert, wie in den Versionshinweisen angegeben:

Optionale Werte werden nicht mehr implizit als wahr ausgewertet, wenn sie einen Wert haben, und als falsch, wenn dies nicht der Fall ist, um Verwirrung bei der Arbeit mit optionalen Bool-Werten zu vermeiden. Führen Sie stattdessen eine explizite Prüfung gegen nil mit den Operatoren == oder! = Durch, um herauszufinden, ob ein optionales Element einen Wert enthält.

Nachtrag: Wie von @MartinR vorgeschlagen, verwendet eine kompaktere Variante der dritten Option den Koaleszenzoperator:

if boolean ?? false {
    // this code runs only if boolean == true
}

Dies bedeutet: Wenn der Boolesche Wert nicht Null ist, wird der Ausdruck als Boolescher Wert ausgewertet (dh unter Verwendung des nicht umschlossenen Booleschen Werts), andernfalls wird der Ausdruck als ausgewertet false

Antonio
quelle
4
Die dritte Option ist die bevorzugte Lösung, da dies die beste Möglichkeit ist, die Absicht des Codes auszudrücken. Verwenden if letwürde auch funktionieren.
Sulthan
28
Eine Variante der dritten Option unter Verwendung des "Null-Koaleszenz-Operators ??" : if boolean ?? false { ... } .
Martin R
4
Aber wenn ich es negieren will, sieht es lächerlich aus if !(boolean ?? true) { ... }:: :(
Andreas
2
Zwangsweise eine äußerst schlechte Idee auspacken. Es sollte immer vermieden werden.
Matthieu Riegler
4
Was ist los mit der ersten Option? Für mich scheint es der beste Weg zu sein.
Vahid Amiri
43

Optionale Bindung

Schnelle 3 & 4

var booleanValue : Bool? = false
if let booleanValue = booleanValue, booleanValue {
    // Executes when booleanValue is not nil and true
    // A new constant "booleanValue: Bool" is defined and set
    print("bound booleanValue: '\(booleanValue)'")
}

Swift 2.2

var booleanValue : Bool? = false
if let booleanValue = booleanValue where booleanValue {
    // Executes when booleanValue is not nil and true
    // A new constant "booleanValue: Bool" is defined and set
    print("bound booleanValue: '\(booleanValue)'")
}

Der Code let booleanValue = booleanValuegibt , falsewenn booleanValueist nilund der ifBlock wird nicht ausgeführt. Wenn dies booleanValuenicht der Fall ist nil, definiert dieser Code eine neue Variable mit dem Namen booleanValuetype Bool(anstelle einer optionalen Variablen Bool?).

Der Swift 3 & 4-Code booleanValue(und der Swift 2.2-Code where booleanValue) wertet die neue booleanValue: BoolVariable aus. Wenn dies der Fall ist, wird der ifBlock mit der neu definierten booleanValue: BoolVariablen im Gültigkeitsbereich ausgeführt (sodass die Option den gebundenen Wert innerhalb des ifBlocks erneut referenzieren kann ).

Hinweis: Es ist eine Swift-Konvention, die gebundene Konstante / Variable genauso zu benennen wie die optionale Konstante / Variable wie z let booleanValue = booleanValue. Diese Technik wird als variable Abschattung bezeichnet . Sie könnten von der Konvention abweichen und so etwas verwenden let unwrappedBooleanValue = booleanValue, unwrappedBooleanValue. Ich weise darauf hin, um zu verstehen, was passiert. Ich empfehle die Verwendung von variablem Shadowing.

 

Andere Ansätze

Nichts verschmilzt

Für diesen speziellen Fall ist keine Koaleszenz klar

var booleanValue : Bool? = false
if booleanValue ?? false {
    // executes when booleanValue is true
    print("optional booleanValue: '\(booleanValue)'")
}

Das Überprüfen auf falseist nicht so klar

var booleanValue : Bool? = false
if !(booleanValue ?? false) {
    // executes when booleanValue is false
    print("optional booleanValue: '\(booleanValue)'")
}

Hinweis: if !booleanValue ?? falseKompiliert nicht.

 

Optionales Auspacken erzwingen (vermeiden)

Das erzwungene Auspacken erhöht die Wahrscheinlichkeit, dass jemand in Zukunft eine Änderung vornimmt, die kompiliert wird, aber zur Laufzeit abstürzt. Daher würde ich so etwas vermeiden:

var booleanValue : Bool? = false
if booleanValue != nil && booleanValue! {
    // executes when booleanValue is true
    print("optional booleanValue: '\(booleanValue)'")
}

 

Ein allgemeiner Ansatz

Obwohl diese Stack - Überlauf Frage ausdrücklich fragt , wie zu überprüfen , ob eine Bool?ist truein einer ifAussage, ist es hilfreich , einen allgemeinen Ansatz zu erkennen , ob für eine echte Kontrolle, falschen oder den ungeöffneten Wert mit anderen Ausdrücken kombinieren.

Wenn der Ausdruck komplizierter wird, finde ich den optionalen Bindungsansatz flexibler und verständlicher als andere Ansätze. Beachten Sie, dass optionale Bindung funktioniert mit jedem optional Typ ( Int?, String?usw.).

Mobiler Dan
quelle
Ich habe Schwierigkeiten, boolesche Ausdrücke mit einer optionalen for while-Schleife zu verwenden. Der Null-Koaleszenz-Operator funktioniert, ist jedoch unübersichtlich und fehleranfällig. Gibt es eine Möglichkeit zu verwenden if let?
Jbaraga
@jbaraga, bitte poste ein Beispiel für die while-Schleife, über die du dich wunderst.
Mobile Dan
Wenn ich ein Array als Stapel verwende, möchte ich Werte entfernen, bis eine Bedingung erfüllt ist oder der Stapel leer ist. Zum Beispielwhile array.last < threshold { array.removeLast() }
jbaraga
Sie können diese Stapelverarbeitung folgendermaßen durchführen if, let, where: while let last = array.last where last < threshold { array.removeLast() }in Swift 2 oder while let last = array.last, last < threshold { array.removeLast() }in Swift 3.
Mobile Dan
Das ist besser, danke. Ich war mir nicht bewusst while let.
Jbaraga
1
var enabled: Bool? = true

if let enabled = enabled, enabled == true {
    print("when is defined and true at the same moment")
}

if enabled ?? false {
    print("when is defined and true at the same moment")
}

if enabled == .some(true) {
    print("when is defined and true at the same moment")
}

if enabled == (true) {
    print("when is defined and true at the same moment")
}

if case .some(true) = enabled {
    print("when is defined and true at the same moment")
}

if enabled == .some(false) {
    print("when is defined and false at the same moment")
}

if enabled == (false) {
    print("when is defined and false at the same moment")
}

if enabled == .none {
    print("when is not defined")
}

if enabled == nil {
    print("when is not defined")
}
Adobels
quelle
0

Ich habe eine andere Lösung gefunden, die die Booleschen Operatoren überlastet. Beispielsweise:

public func < <T: Comparable> (left: T?, right: T) -> Bool {
    if let left = left {
        return left < right
    }
    return false
}

Dies ist möglicherweise nicht ganz im "Geist" der Sprachänderungen, ermöglicht jedoch das sichere Auspacken von Optionen und kann für Bedingungen überall verwendet werden, auch für while-Schleifen.

jbaraga
quelle
1
Wenn Sie auf den ursprünglichen Beitrag zurückblicken, wird diese spezielle Frage nicht beantwortet, sondern die Frage, die ich in meinem früheren Kommentar aufgeworfen habe.
Jbaraga
Ich würde bei der Verwendung dieser Überladung sehr vorsichtig sein, da es Fälle geben kann, in denen Sie nicht möchten, dass Null als "größer als" ein Nicht-Null-Wert behandelt wird (in bestimmten Kontexten möchten Sie möglicherweise das gegenteilige Ergebnis oder möglicherweise eine Alternative Handhabung vollständig). Wenn Sie stattdessen das normale Entpacken verwenden, müssen Sie explizit angeben, wie Sie in jedem Fall mit Nullen umgehen möchten, damit Sie weniger auf unerwartete Ergebnisse stoßen.
John Montgomery
0

Die Antwort, die ich am einfachsten zu lesen fand, ist das Definieren einer Funktion. Nicht sehr kompliziert, macht aber die Arbeit.

func isTrue(_ bool: Bool?) -> Bool {
    guard let b = bool else {
        return false
    }
    return b
}

Verwendung:

let b: Bool? = true
if isTrue(b) {
    // b exists and is true
} else {
    // b does either not exist or is false
}
Charel ctk
quelle
0

Wie Antonio sagte

Optionale Werte werden nicht mehr implizit als wahr ausgewertet, wenn sie einen Wert haben, und als falsch, wenn dies nicht der Fall ist, um Verwirrung bei der Arbeit mit optionalen Bool-Werten zu vermeiden. Führen Sie stattdessen eine explizite Prüfung gegen nil mit den Operatoren == oder! = Durch, um herauszufinden, ob ein optionales Element einen Wert enthält.

Ich habe ein paar Stunden damit verbracht, eine Codezeile zu verstehen, auf die ich gestoßen bin, aber dieser Thread hat mich auf den richtigen Weg gebracht.

Dieses Zitat stammt aus dem August 2014 und wurde seitdem von Apple Nevernach dem Vorschlag SE-0102 eingeführt. Letzterer hat es an Equatable, Hashable, Error und Comparable angepasst

Es ist jetzt möglich zu überprüfen, ob ein Boolescher Wert nilverwendet Never?:


var boolean: Bool? = false
boolean is Never? // false
boolean = true
boolean is Never? // false
boolean = nil
boolean is Never? // true

Sie können tatsächlich alle anderen unbewohnbaren Typen verwenden:

public enum NeverEver { }
var boolean: Bool? = false
boolean is NeverEver? // false
boolean = true
boolean is NeverEver? // false
boolean = nil
boolean is NeverEver? // true

Abgesehen davon ist es jetzt auch möglich, einen Property Wrapper zu verwenden :

@propertyWrapper struct OptionalBool {
    public var wrappedValue: Bool?
    public var projectedValue: Bool { wrappedValue ?? false }
    public init(wrappedValue: Bool?) {
        self.wrappedValue = wrappedValue
    }
}

struct Struct {
    @OptionalBool var predicate: Bool?
    var description: String {
        if $predicate {
            return "predicate is true"
        }
        return "predicate is false"
    }
}

var object = Struct()
object.description // "predicate is false"
object.predicate = false
object.description // "predicate is false"
object.predicate = true
object.description // "predicate is true"

oder auch:

@propertyWrapper struct OptionalBool {
    var wrappedValue: Bool?
    var projectedValue: OptionalBool { self }
    var isNil: Bool { wrappedValue is Never? }
    var value: Bool { wrappedValue ?? false }
    
    init(wrappedValue: Bool?) {
        self.wrappedValue = wrappedValue
    }
}

struct Struct {
    @OptionalBool var predicate: Bool?
    var description: String {
        if $predicate.value {
            return "predicate is true"
        }
        if !$predicate.isNil {
            return "predicate is false"
        }
        return "predicate is nil"
    }
}

var object = Struct()
object.description // "predicate is nil"
object.predicate = false
object.description // "predicate is false"
object.predicate = true
object.description // "predicate is true"
AnderCover
quelle