【问题标题】:Checking the value of an Optional Bool检查可选布尔值
【发布时间】:2014-08-27 09:18:23
【问题描述】:

当我想检查 Optional Bool 是否为真时,这样做是行不通的:

var boolean : Bool? = false
if boolean{
}

这会导致这个错误:

可选类型'@IvalueBool?'不能用作布尔值;测试'!= nil' 而是

我不想检查 nil;我想检查返回的值是否为真。

如果我使用 Optional Bool,我是否总是需要使用 if boolean == true

既然 Optionals 不再符合BooleanType,编译器不应该知道我要检查 Bool 的值吗?

【问题讨论】:

  • 由于布尔值符合 Equatable 协议,因此您可以将可选项与非可选项进行比较。见here

标签: swift boolean optional


【解决方案1】:

对于可选的布尔值,需要明确检查:

if boolean == true {
    ...
}

否则你可以打开可选的:

if boolean! {
    ...
}

但是如果 boolean 为 nil,则会生成运行时异常 - 以防止发生这种情况:

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

在 beta 5 之前它是可能的,但它已按照发行说明中的​​报告进行了更改:

当它们有值时,Optionals 不再隐式地计算为 true,而当它们没有值时,它们不再隐式地计算为 false,以避免在使用可选的 Bool 值时产生混淆。相反,请使用 == 或 != 运算符对 nil 进行显式检查,以确定可选项是否包含值。

附录:正如@MartinR 所建议的,第三个选项的更紧凑的变体是使用合并运算符:

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

这意味着:如果 boolean 不是 nil,则表达式计算为布尔值(即使用未包装的布尔值),否则表达式计算为 false

【讨论】:

  • 第三个选项是首选方案,因为它是表达代码意图的最佳方式。使用if let 也可以。
  • 第三个选项的变体,使用 "nil coalescing operator ??": if boolean ?? false { ... } .
  • 但如果我想否定它,它开始看起来很荒谬:if !(boolean ?? true) { ... } :(
  • 强制解开一个最糟糕的主意。应始终避免。
  • 第一个选项有什么问题?对我来说,这似乎是最好的方式。
【解决方案2】:

可选绑定

斯威夫特 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)'")
}

斯威夫特 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)'")
}

如果booleanValuenil 并且if 块不执行,则代码let booleanValue = booleanValue 返回false。如果booleanValue 不是nil,则此代码定义一个名为booleanValue 的新变量,类型为Bool(而不是可选的Bool?)。

Swift 3 和 4 代码 booleanValue(和 Swift 2.2 代码 where booleanValue)计算新的 booleanValue: Bool 变量。如果为真,if 块将使用范围内新定义的booleanValue: Bool 变量执行(允许选项在if 块内再次引用绑定值)。

注意:将绑定的常量/变量命名为与可选常量/变量相同的名称是 Swift 约定,例如 let booleanValue = booleanValue。这种技术称为可变阴影。你可以打破常规,使用let unwrappedBooleanValue = booleanValue, unwrappedBooleanValue 之类的东西。我指出这一点是为了帮助理解正在发生的事情。我建议使用可变阴影。

 

其他方法

零合并

对于这种特定情况,零合并很明显

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

检查false 不是很清楚

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

注意:if !booleanValue ?? false 无法编译。

 

强制展开可选(避免)

强制解包增加了有人在未来进行更改以编译但在运行时崩溃的机会。因此,我会避免这样的事情:

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

 

一般方法

虽然这个堆栈溢出问题专门询问如何在if 语句中检查Bool? 是否为true,但确定一个通用方法是否检查真、假或将未包装的值与其他表达式组合是有帮助的.

随着表达式变得越来越复杂,我发现可选绑定方法比其他方法更灵活且更易于理解。请注意,可选绑定适用于任何可选类型(Int?String? 等)。

【讨论】:

  • 我在使用带有可选 for while 循环的布尔表达式时遇到了困难。 nil 合并运算符有效,但它很混乱且容易出错。有没有办法使用if let
  • @jbaraga,请发布您想知道的 while 循环示例。
  • 在将数组用作堆栈时,我想弹出值直到满足条件或堆栈为空。例如,while array.last < threshold { array.removeLast() }
  • 您可以使用if, let, where 完成堆栈处理,使用以下命令:Swift 2 中的while let last = array.last where last < threshold { array.removeLast() } 或Swift 3 中的while let last = array.last, last < threshold { array.removeLast() }
  • 这样更好,谢谢。我不知道while let
【解决方案3】:
var enabled: Bool? = true

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

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

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

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

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

if let enabled = enabled, !enabled {
    print("when is defined and false 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")
}

【讨论】:

  • 有没有人知道关于推荐哪种形式的讨论资源?
【解决方案4】:

我找到了另一个解决方案,重载布尔运算符。例如:

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

这可能并不完全符合语言更改的“精神”,但它允许安全地展开可选选项,并且可用于任何地方的条件,包括 while 循环。

【讨论】:

  • 抱歉,回看原帖,它并没有回答那个具体的问题,而是我在之前的评论中提出的问题。
  • 我会非常小心地使用这个重载,因为在某些情况下您可能不希望 nil 被视为“大于”非 nil 值(您可能想要相反的导致某些上下文,或可能完全替代处理)。使用正常展开会强制您明确说明在每种情况下如何处理 nil,因此您不太可能遇到意外结果。
【解决方案5】:

我发现最容易阅读的答案是定义一个函数。不是很复杂,但确实有效。

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

用法:

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

【讨论】:

    【解决方案6】:

    正如Antonio所说的

    当它们有值时,Optionals 不再隐式地计算为 true,而当它们没有值时,它们不再隐式地计算为 false,以避免在使用可选的 Bool 值时产生混淆。相反,请使用 == 或 != 运算符对 nil 进行显式检查,以查明可选项是否包含值。

    我花了几个小时试图理解我偶然发现的一行代码,但是这个线程让我走上了正确的轨道。

    这句话来自august 2014,从那时起Apple在proposal SE-0102之后引入了Never,后者使其符合Equatable, Hashable, Error and Comparable

    现在可以使用Never? 检查布尔值是否为nil

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

    您实际上可以使用任何其他无法居住的类型:

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

    话虽如此,现在也可以使用property wrapper

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

    甚至:

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

    【讨论】:

      猜你喜欢
      • 2017-01-22
      • 1970-01-01
      • 2022-01-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-09
      • 2019-02-23
      • 2016-08-22
      相关资源
      最近更新 更多