Swift中开关盒的详尽条件
Posted
技术标签:
【中文标题】Swift中开关盒的详尽条件【英文标题】:Exhaustive condition of switch case in Swift 【发布时间】: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 编译,所以编译器变得更聪明了:) ...但它在运行时崩溃:( 【参考方案1】:Swift 仅在使用 enum
类型时才真正验证 switch
块是否详尽无遗。除了true
和false
之外,即使打开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 体验了这一点,并认为我错过了一些东西。我提交了一个错误报告:19582311switch
不再需要 default
用于 Bool
如果您有 true
和 false
的案例。我不确定这个变化是什么时候发生的,但是在 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
的情况下工作。如果 true
和 false
都被覆盖,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
【讨论】:
以上是关于Swift中开关盒的详尽条件的主要内容,如果未能解决你的问题,请参考以下文章