Vérification de la valeur d'un booléen facultatif

88

Lorsque je veux vérifier si un booléen facultatif est vrai, cela ne fonctionne pas:

var boolean : Bool? = false
if boolean{
}

Il en résulte cette erreur:

Type facultatif '@IvalueBool?' ne peut pas être utilisé comme booléen; testez plutôt '! = nil'

Je ne veux pas vérifier zéro; Je veux vérifier si la valeur renvoyée est vraie.

Dois-je toujours faire if boolean == truesi je travaille avec un Bool facultatif?

Puisque les options ne sont BooleanTypeplus conformes à , le compilateur ne devrait-il pas savoir que je veux vérifier la valeur du booléen?

Chat de lune
la source
Puisque les booléens sont conformes au protocole Equatable, vous pouvez comparer un optionnel à un non optionnel. Voir ici
Honey

Réponses:

192

Avec des booléens facultatifs, il est nécessaire de rendre la vérification explicite:

if boolean == true {
    ...
}

Sinon, vous pouvez déballer l'option:

if boolean! {
    ...
}

Mais cela génère une exception d'exécution si booléen est nil- pour éviter que:

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

Avant la bêta 5, c'était possible, mais cela a été modifié comme indiqué dans les notes de publication:

Les options ne sont plus implicitement évaluées à true lorsqu'elles ont une valeur et à false lorsqu'elles ne le sont pas, pour éviter toute confusion lors de l'utilisation de valeurs Bool facultatives. À la place, effectuez une vérification explicite contre nil avec les opérateurs == ou! = Pour savoir si un optionnel contient une valeur.

Addendum: comme suggéré par @MartinR, une variante plus compacte de la 3ème option utilise l'opérateur de fusion:

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

ce qui signifie: si booléen n'est pas nul, l'expression prend la valeur booléenne (c'est-à-dire en utilisant la valeur booléenne non emballée), sinon l'expression prend la valeur false

Antonio
la source
4
La troisième option est la solution préférée car c'est la meilleure façon d'exprimer l'intention du code. L'utilisation if letfonctionnerait également.
Sulthan
29
Une variante de la troisième option, en utilisant le « opérateur coalescent nul ?? »: if boolean ?? false { ... } .
Martin R
4
Mais si je veux le nier, cela commence à paraître ridicule: if !(boolean ?? true) { ... }:(
Andreas
2
Développer forcé une très mauvaise idée. Cela devrait toujours être évité.
Matthieu Riegler
4
Quel est le problème avec la première option? Cela me semble être le meilleur moyen.
Vahid Amiri
43

Reliure facultative

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

Le code let booleanValue = booleanValuerenvoie falseif booleanValueis nilet le ifbloc ne s'exécute pas. Si ce booleanValuen'est pas le cas nil, ce code définit une nouvelle variable nommée booleanValuede type Bool(au lieu d'un optionnel, Bool?).

Le code Swift 3 & 4 booleanValue(et le code Swift 2.2 where booleanValue) évalue la nouvelle booleanValue: Boolvariable. Si c'est vrai, le ifbloc s'exécute avec la booleanValue: Boolvariable nouvellement définie dans la portée (permettant à l'option de référencer à nouveau la valeur liée dans le ifbloc).

Remarque: C'est une convention Swift de nommer la constante / variable liée de la même manière que la constante / variable optionnelle telle que let booleanValue = booleanValue. Cette technique s'appelle l' ombre variable . Vous pouvez rompre avec la convention et utiliser quelque chose comme let unwrappedBooleanValue = booleanValue, unwrappedBooleanValue. Je le souligne pour aider à comprendre ce qui se passe. Je recommande d'utiliser l'ombre variable.

 

Autres approches

Nil coalescence

Aucune fusion n'est claire pour ce cas spécifique

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

La vérification falsen'est pas aussi claire

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

Remarque: if !booleanValue ?? falsene compile pas.

 

Forcer le déballage en option (à éviter)

Le déballage forcé augmente les chances que quelqu'un fasse un changement dans le futur qui se compile mais plante au moment de l'exécution. Par conséquent, j'éviterais quelque chose comme ceci:

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

 

Une approche générale

Bien que cette question de débordement de pile demande spécifiquement comment vérifier si a se Bool?trouve truedans une ifinstruction, il est utile d'identifier une approche générale, qu'il s'agisse de vérifier la valeur true, false ou de combiner la valeur déballée avec d'autres expressions.

Au fur et à mesure que l'expression se complique, je trouve l'approche de liaison facultative plus flexible et plus facile à comprendre que les autres approches. Notez que les travaux de liaison en option avec tout type en option ( Int?, String?, etc.).

Mobile Dan
la source
J'ai des difficultés à utiliser des expressions booléennes avec des boucles optionnelles for while. L'opérateur de fusion nil fonctionne, mais il est désordonné et sujet aux erreurs. Y a-t-il un moyen d'utiliser if let?
jbaraga
@jbaraga, veuillez poster un exemple de la boucle while sur laquelle vous vous interrogez.
Mobile Dan
En utilisant un tableau comme pile, je veux faire apparaître des valeurs jusqu'à ce qu'une condition soit remplie ou que la pile soit vide. Par exemple,while array.last < threshold { array.removeLast() }
jbaraga
Vous pouvez accomplir ce traitement de pile en if, let, whereutilisant ceci: while let last = array.last where last < threshold { array.removeLast() }dans Swift 2 ou while let last = array.last, last < threshold { array.removeLast() }dans Swift 3.
Mobile Dan
C'est mieux, merci. Je n'étais pas au courant 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")
}
Blazej SLEBODA
la source
0

J'ai trouvé une autre solution, surchargeant les opérateurs booléens. Par exemple:

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

Cela peut ne pas être totalement dans l '«esprit» des changements de langage, mais cela permet de déballer en toute sécurité les options, et il est utilisable pour les conditions n'importe où, y compris les boucles while.

jbaraga
la source
1
Désolé, en regardant le post original, il ne répond pas à cette question spécifique, mais plutôt à la question que j'ai soulevée dans mon commentaire précédent.
jbaraga
Je ferais très attention d'utiliser cette surcharge, car il peut y avoir des cas où vous ne voulez pas que nul soit traité comme "supérieur à" une valeur non nulle (vous pouvez souhaiter le résultat opposé dans certains contextes, ou éventuellement manipulation entièrement). L'utilisation du déballage normal vous oblige à aborder explicitement la manière dont vous souhaitez gérer les nils dans tous les cas, de sorte que vous êtes moins susceptible de rencontrer des résultats inattendus.
John Montgomery
0

La réponse que j'ai trouvée la plus facile à lire est de définir une fonction. Pas très compliqué mais fait le travail.

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

usage:

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

Comme l'a dit Antonio

Les options ne sont plus implicitement évaluées à true lorsqu'elles ont une valeur et à false lorsqu'elles ne le sont pas, pour éviter toute confusion lors de l'utilisation de valeurs Bool facultatives. À la place, effectuez une vérification explicite contre nil avec les opérateurs == ou! = Pour savoir si un optionnel contient une valeur.

J'ai passé quelques heures à essayer de comprendre une ligne de code sur laquelle je suis tombé par hasard, mais ce fil m'a mis sur la bonne voie.

Cette citation est d' août 2014 , et depuis lors , Apple a présenté la Neversuite proposition SE-0102 et celle- ci a rendu conforme à assimilables, HASHABLE, erreur et Comparable

Il est maintenant possible de vérifier si un booléen nilutilise Never?:


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

Vous pouvez en fait utiliser tout autre type inhabitable :

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

Cela étant dit, il est également possible d'utiliser un wrapper de propriété maintenant:

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

ou même:

@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
la source