我必须在 didSet 中的 switch 语句中解包可选两次

I have to unwrap optional twice in switch statement inside didSet

var test: Int! {
    didSet {
        switch test {
        case 1: print("one")
        case 2: print("two")
        default: print("something else")
        }
    }
}

var toupleTest: (one: Int, two: Int)! {
    didSet {
        switch toupleTest! {
        case (1, 1): print("one, one")
        case (2, 2): print("two, two")
        default: print("something else")
        }
    }
}

test = 2 // prints "two"
toupleTest = (1, 1) // prints "one, one"

在 Int 值的情况下一切正常。
但在元组的情况下,我必须解开可选的两次!否则我会遇到编译错误。
看起来 swift 逻辑不一致。或者这是一个错误?

我认为这是因为元组是 swift 中的值类型。默认类型也是默认类型。基本上你的 unwrap 什么都不做,你只是标记这个值将是非可选的。但是要访问内部的值,你必须打开你的可选元组并达到值

不一致,但是原因有点复杂

为了在 switch 语句中使用类型,它需要符合 Equatable.

使用新的 struct 类型查看此示例:

struct MyType: Equatable {
    let x: Int
    
    static func ==(lhs: MyType, rhs: MyType) -> Bool {
        return lhs.x == rhs.x
    }
}

var mytypeTest: MyType! {
    didSet {
        switch mytypeTest {
        case MyType(x: 1): print("one")
        case MyType(x: 2): print("two")
        default: print("something else")
        }
    }
}

mytypeTest = MyType(x: 1)

这有效,但是如果您从 MyType 中删除 : Equatable,您将得到错误 Operator function '~=` requires that 'MyType' conform to 'Equatable'.

这是第一个提示。 switch使用~=运算符进行比较,类型必须是Equatable.

那么如果我们尝试使用'~='比较两个元组会发生什么:

if (1, 3) ~= (1, 3) {
    print("same")
}

这给出了错误:Type '(Int, Int)' cannot conform to 'Equatable';只有 struct/enum/class 类型可以符合协议 .

因此,这意味着不能在 switch 中使用元组,我们知道这不是真的。

嗯,元组在 switch 中有一个特殊的位置,它们在 模式匹配 中用于解构元组。例如:

let a = (1, 2)

switch a {
case let (x, y):
    print("the tuple values are \(x) and \(y)")
}

这会打印 the tuple values are 1 and 2.

因此,元组在 switch 中用于使用 模式匹配 进行匹配和解构。所以你可以在 switch 中使用元组,即使它不符合 Equatable 因为它有这种特殊用途。

您的示例的问题在于模式与您打开的类型不匹配。您的值的类型是 (Int, Int)?,模式是 (Int, Int).

那么,如何在不强制展开元组值的情况下解决这个问题?通过将 ? 添加到模式来更改您的模式以匹配:

var toupleTest: (one: Int, two: Int)! {
    didSet {
        switch toupleTest {
        case (1, 1)?: print("one, one")
        case (2, 2)?: print("two, two")
        default: print("something else")
        }
    }
}

注意:添加 ? 也适用于您的 Int 示例:

var test: Int! {
    didSet {
        switch test {
        case 1?: print("one")
        case 2?: print("two")
        default: print("something else")
        }
    }
}

但这不是必需的,因为 IntEquatable 并且 Swift 知道如何比较 Int?Int 是否相等。