ios Swift 中 switch case 的穷举条件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26686542/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 03:27:29  来源:igfitidea点击:

Exhaustive condition of switch case in Swift

iosswift

提问by Girish Nair

The Apple documentationsays

苹果文档

Every switch statement must be exhaustive. That is, every possible value of the type being considered must be matched by one of the switch cases.

每个 switch 语句都必须详尽无遗。也就是说,所考虑的类型的每个可能值都必须与其中一个 switch case 匹配。

So in new Xcode I have placed a code like this

所以在新的 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();
}

Now if i remove the default section I get a compiler error:

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

Switch must be exhaustive
Do you want to add missing cases? Fix

Switch 必须详尽无遗
您要添加缺失的案例吗?使固定

So my question is for a case that I have mentioned as case 0...65535:have I not mentioned all the case values for an UInt16?? But still I am getting an error ?? Why am I getting this error, Did i miss something ??

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

回答by Nate Cook

Swift only truly verifies that a switchblock is exhaustive when working with enumtypes. Even a switching on Boolrequires a defaultblock in addition to trueand false:

Swift 仅switch在处理enum类型时真正验证块是否详尽。除了和之外,即使打开也Bool需要一个default块:truefalse

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

With an enum, however, the compiler is happy to only look at the two cases:

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

enum MyBool {
    case True
    case False
}

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

If you need to include a defaultblock for the compiler's sake but don't have anything for it to do, the breakkeyword comes in handy:

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

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

回答by rickster

Part of why you see that error because the compiler can't verify that switch is exhaustive without running code. The expression 0...65535creates a ClosedIntervalstruct, and when the switch statement executes it has to ask that struct if the value quantityis in the interval. There's room for that to change at run time, so the compiler can't check it at compile time. (See the Halting Problem.)

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

More generally, the compiler can't detect an exhaustive switch for integer values — even if you add specific cases for every integer value (case 0: ... case 1: ... ... case 65535:), it doesn't know your switch is exhaustive. (Theoretically it could, though: consider filing a feature requestabout this if it's something you'd like to see.)

更一般地说,编译器无法检测到整数值的详尽开关——即使您为每个整数值 ( case 0: ... case 1: ... ... case 65535:)添加特定情况,它也不知道您的开关是详尽的。(理论上它可以,但是:如果您想看到它,请考虑提交有关此的功能请求。)

As it stands, there are two scenarios where Swift can detect completeness and allow you to omit the defaultclause: enums and value binding in tuples. @NateCook's answer covers enums — if you switch on an enum value and have a casein your switchfor every casein the enum, you don't need a default. You also don't need a defaultlabel if you switch on a tuple and bind every possible combination of values, as seen in the Swift book:

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

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

You might generalize this rule as "if the type system knows about the possible values of your type, it can detect switchcompleteness", but the fact that there's a level on which the type system doesn't know the range of possible (e.g.) UInt32values is sort of splitting hairs...

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

回答by Gurjinder Singh

Swift 4.1. Either you need to specify all cases or Just include default block inside switch statement.

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

回答by Kenster999

(As of Swift 4.2, and probably earlier): I have a helper function that converts a Bool?into the selectedSegmentIndexfor a UISegmentedControl with 2 segments. If the value is nilthen neither segment should be selected. My function uses a switch, which returns the appropriate segment index for true or false values, and uses this to explicitly test for the niland satisfy the compiler's need for it to be exhaustive:

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

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

Technically, the case nil:fallthroughisn't required because the default:will suffice, but this syntax may be useful if you want to explicitly test a value to make the code more self-documenting, or perhaps in another situation.

从技术上讲,这case nil:fallthrough不是必需的,因为default:它就足够了,但是如果您想显式测试一个值以使代码更具自文档性,或者在其他情况下,此语法可能很有用。