Verifica del valore di un Bool facoltativo


89

Quando voglio controllare se un Bool opzionale è vero, questa operazione non funziona:

var boolean : Bool? = false
if boolean{
}

Risulta in questo errore:

Tipo facoltativo "@IvalueBool?" non può essere utilizzato come booleano; prova invece per "! = nil"

Non voglio verificare la presenza di zero; Voglio controllare se il valore restituito è vero.

Devo sempre farlo if boolean == truese lavoro con un Bool opzionale?

Dato che gli Optionals non sono BooleanTypepiù conformi a , il compilatore non dovrebbe sapere che voglio controllare il valore di Bool?


Poiché i booleani sono conformi al protocollo Equatable, è possibile confrontare un facoltativo con uno non facoltativo. Vedi qui
Honey

Risposte:


197

Con booleani opzionali è necessario rendere esplicito il controllo:

if boolean == true {
    ...
}

Altrimenti puoi scartare l'opzionale:

if boolean! {
    ...
}

Ma questo genera un'eccezione di runtime se booleano è nil- per evitare che:

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

Prima della beta 5 era possibile, ma è stato modificato come riportato nelle note di rilascio:

Gli optionals non valutano più implicitamente true quando hanno un valore e false quando non lo hanno, per evitare confusione quando si lavora con valori Bool facoltativi. Invece, fai un controllo esplicito su zero con gli operatori == o! = Per scoprire se un opzionale contiene un valore.

Addendum: come suggerito da @MartinR, una variazione più compatta alla terza opzione sta usando l'operatore di coalescenza:

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

che significa: se booleano non è nullo, l'espressione restituisce il valore booleano (cioè utilizzando il valore booleano scartato), altrimenti l'espressione restituisce false


4
La terza opzione è la soluzione preferita perché è il modo migliore per esprimere l'intenzione del codice. Anche l'utilizzo if letfunzionerebbe.
Sulthan

29
Una variante della terza opzione, utilizzando il "operatore coalescenza nil ??": if boolean ?? false { ... } .
Martin R

4
Ma se voglio negarlo inizia a sembrare ridicolo: if !(boolean ?? true) { ... }:(
Andreas

2
Scartare forzatamente un'idea assolutamente cattiva. Dovrebbe essere sempre evitato.
Matthieu Riegler

5
Cosa c'è di sbagliato nella prima opzione? A me sembra il modo migliore.
Vahid Amiri

43

Rilegatura opzionale

Swift 3 e 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)'")
}

Il codice let booleanValue = booleanValuerestituisce falseif booleanValueis nile il ifblocco non viene eseguito. In caso booleanValuecontrario nil, questo codice definisce una nuova variabile denominata booleanValuedi tipo Bool(invece di un'opzione, Bool?).

Il codice Swift 3 e 4 booleanValue(e il codice Swift 2.2 where booleanValue) valuta la nuova booleanValue: Boolvariabile. Se è vero, il ifblocco viene eseguito con la nuova booleanValue: Boolvariabile definita nell'ambito (consentendo all'opzione di fare nuovamente riferimento al valore associato all'interno del ifblocco).

Nota: è una convenzione di Swift denominare la costante / variabile associata allo stesso modo della costante / variabile opzionale come let booleanValue = booleanValue. Questa tecnica è chiamata ombreggiatura variabile . Potresti rompere con le convenzioni e usare qualcosa di simile let unwrappedBooleanValue = booleanValue, unwrappedBooleanValue. Lo faccio notare per aiutare a capire cosa sta succedendo. Consiglio di utilizzare l'ombreggiatura variabile.

 

Altri approcci

Niente coalescenza

La coalescenza nulla è chiara per questo caso specifico

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

Il controllo falsenon è così chiaro

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

Nota: if !booleanValue ?? falsenon compila.

 

Forza scartare opzionale (evitare)

Lo scartamento forzato aumenta la possibilità che qualcuno apporti in futuro un cambiamento che compila ma si arresta in modo anomalo in fase di esecuzione. Pertanto, eviterei qualcosa del genere:

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

 

Un approccio generale

Sebbene questa domanda sull'overflow dello stack chieda specificamente come verificare se a Bool?è trueall'interno di ifun'istruzione, è utile identificare un approccio generale che si tratti di verificare se è vero, falso o combinare il valore non racchiuso con altre espressioni.

Man mano che l'espressione diventa più complicata, trovo l'approccio di associazione facoltativa più flessibile e più facile da capire rispetto ad altri approcci. Si noti che le opere di legame opzionali con qualsiasi tipo opzionale ( Int?, String?, ecc).


Ho difficoltà a utilizzare espressioni booleane con un ciclo while opzionale. L'operatore di coalescenza nullo funziona, ma è disordinato e soggetto a errori. C'è un modo per usare if let?
jbaraga

@jbaraga, per favore pubblica un esempio del ciclo while che ti stai chiedendo.
Mobile Dan

Utilizzando un array come stack, desidero estrarre i valori fino a quando non viene soddisfatta una condizione o lo stack è vuoto. Ad esempio,while array.last < threshold { array.removeLast() }
jbaraga

Puoi completare l'elaborazione dello stack if, let, whereutilizzando questo: while let last = array.last where last < threshold { array.removeLast() }in Swift 2 o while let last = array.last, last < threshold { array.removeLast() }in Swift 3.
Mobile Dan

Va meglio, grazie. Non ero a conoscenza while let.
jbaraga

2
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")
}

0

Ho trovato un'altra soluzione, sovraccaricando gli operatori booleani. Per esempio:

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

Questo potrebbe non essere totalmente nello "spirito" dei cambiamenti linguistici, ma consente di scartare in modo sicuro gli optionals, ed è utilizzabile per i condizionali ovunque, inclusi i cicli while.


1
Scusate, guardando indietro al post originale, non risponde a quella domanda specifica, ma piuttosto alla domanda che ho sollevato nel mio commento precedente.
jbaraga

Farei molta attenzione nell'usare questo sovraccarico, perché potrebbero esserci casi in cui non vuoi che zero venga trattato come "maggiore" di un valore diverso da zero (potresti volere il risultato opposto in determinati contesti, o forse un'alternativa maneggiando interamente). L'uso del normale scartamento invece ti costringe ad affrontare esplicitamente il modo in cui vuoi gestire i nil in ogni caso, quindi è meno probabile che tu incorra in risultati imprevisti.
John Montgomery

0

La risposta che ho trovato più facile da leggere è definire una funzione. Non è molto complicato ma fa il lavoro.

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

utilizzo:

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

0

Come ha detto Antonio

Gli optionals non valutano più implicitamente true quando hanno un valore e false quando non lo hanno, per evitare confusione quando si lavora con valori Bool facoltativi. Invece, fai un controllo esplicito su zero con gli operatori == o! = Per scoprire se un opzionale contiene un valore.

Ho passato alcune ore a cercare di capire una riga di codice in cui sono incappato, ma questo thread mi ha messo sulla strada giusta.

Questa citazione è di agosto 2014 e da allora Apple ha introdotto la Neverseguente proposta SE-0102 e quest'ultima l'ha resa conforme a Equatable, Hashable, Error e Comparable

È ora possibile verificare se un booleano sta nilusando Never?:


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

Puoi effettivamente utilizzare qualsiasi altro tipo inabitabile :

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

Detto questo, è anche possibile utilizzare un wrapper di proprietà ora:

@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"

o anche:

@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"

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.