【问题标题】:Exhaustive condition of switch case in SwiftSwift中开关盒的详尽条件
【发布时间】:2014-11-01 05:41:38
【问题描述】:

苹果documentation

每个 switch 语句都必须是详尽的。也就是说,所有可能的 正在考虑的类型的值必须与以下之一匹配 切换案例。

所以在新的 Xcode 中我放置了这样的代码

println(UInt16.min); // Output : '0'
println(UInt16.max); // Output : '65535'

var quantity : UInt16 = 10;

switch quantity {
case 0...65535: //OR case UInt16.min...UInt16.max:
    println();
default:
    println();
}

现在,如果我删除默认部分,我会收到编译器错误:

切换必须是详尽的
您要添加缺失的案例吗?修复

所以我的问题是针对我提到的 case 0...65535: 的案例,我没有提到 UInt16 的所有案例值吗?但我仍然收到错误??为什么会出现这个错误,我错过了什么吗??

【问题讨论】:

  • 这段代码用 Xcode 7.3/Swift 2.2 编译,所以编译器变得更聪明了:)
  • ...但它在运行时崩溃:(

标签: ios swift


【解决方案1】:

Swift 仅在使用 enum 类型时才真正验证 switch 块是否详尽无遗。除了truefalse 之外,即使打开Bool 也需要default 块:

var b = true
switch b {
case true:  println("true")
case false: println("false")
}
// error: switch must be exhaustive, consider adding a default clause

但是,对于 enum,编译器很乐意只查看两种情况:

enum MyBool {
    case True
    case False
}

var b = MyBool.True
switch b {
case .True:  println("true")
case .False: println("false")
}

如果您为了编译器需要包含一个default 块,但又没有任何事情要做,那么break 关键字就派上用场了:

var b = true
switch b {
case true:  println("true")
case false: println("false")
default: break
}

【讨论】:

  • 所以你的意思是iOS说它详尽,但实际上不是?
  • 我刚刚用非可选的 Bool 体验了这一点,并认为我错过了一些东西。我提交了一个错误报告:19582311
  • switch 不再需要 default 用于 Bool 如果您有 truefalse 的案例。我不确定这个变化是什么时候发生的,但是在 Xcode 7.3 中使用 Swift 2.2,上面的工作。
  • @NateCook 。刚刚检查了 xCode 10,如果存在 true 和 false,则 Bool 不需要默认值。 “错误:开关必须详尽”不会出现
  • 已确认;这个答案在 Swift 中不再适用。我怀疑从 Swift 2 或 3 开始。
【解决方案2】:

您看到该错误的部分原因是编译器无法在不运行代码的情况下验证 switch 是否详尽。表达式0...65535 创建了一个ClosedInterval 结构,当switch 语句执行时,它必须询问该结构quantity 的值是否在区间内。在运行时有更改的空间,因此编译器无法在编译时检查它。 (见Halting Problem。)

更一般地说,编译器无法检测到整数值的穷举开关——即使你为每个整数值添加特定情况 (case 0: ... case 1: ... ... case 65535:),它也不知道你的开关是穷举的。 (但理论上它可以:如果您想看到,请考虑 filing a feature request。)

目前,Swift 可以在两种情况下检测完整性并允许您省略 default 子句:元组中的枚举和值绑定。 @NateCook 的回答涵盖了枚举——如果你打开一个枚举值并且在你的switch 中为枚举中的每个case 有一个case,你就不需要default。如果您打开一个元组并绑定所有可能的值组合,您也不需要default 标签,如in the Swift book 所示:

switch anotherPoint {
case (let x, 0):
    println("on the x-axis with an x value of \(x)")
case (0, let y):
    println("on the y-axis with a y value of \(y)")
case let (x, y):
    println("somewhere else at (\(x), \(y))")
}

您可以将此规则概括为“如果类型系统知道您的类型的可能值,它可以检测到switch 完整性”,但事实上存在一个类型系统不知道范围的级别可能的(例如)UInt32 值有点像分裂的头发......

【讨论】:

  • 不...用UInt8 尝试过,它会提示您添加一个默认大小写,即使是所有 256 个数字。
  • 嗯,这就是我从手机发帖并忘记了我记得一半的内容。 :) 修改后的答案和一些进一步的信息。
  • “编译器无法在不运行代码的情况下验证 switch 是否详尽”——这根本不是真的。
  • 第三种情况现在可以在没有default 的情况下工作。如果 truefalse 都被覆盖,Bool 上的 switch 不需要默认大小写。
【解决方案3】:

斯威夫特 4.1。要么您需要指定所有情况,要么只在 switch 语句中包含默认块。

【讨论】:

    【解决方案4】:

    (从 Swift 4.2 开始,可能更早):我有一个辅助函数,可以将 Bool? 转换为 selectedSegmentIndex 用于具有 2 个段的 UISegmentedControl。如果值为nil,则不应选择任何段。我的函数使用了一个开关,它为真或假值返回适当的段索引,并使用它来显式测试nil 并满足编译器对其详尽无遗的需求:

    case nil:  // only remaining possible value
        fallthrough
    default:
        return UISegmentedControl.noSegment
    

    从技术上讲,case nil: fallthrough 不是必需的,因为 default: 就足够了,但是如果您想显式测试一个值以使代码更具自我记录性,或者可能在另一种情况。

    【讨论】:

      【解决方案5】:

      检查你的枚举是否被初始化为可选项,可以是大小写,也可以是 nil

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多