如何在 switch 语句中(同时)键入匹配和解包可选值?

How to type match and unwrap optional values in a switch statement (simultaneously)?

有没有办法在一个案例中结合类型匹配和展开可选值?

在下面的 playground 代码中有三个循环:

循环 A 正在使用 if let 来匹配类型并同时展开可选值。

循环 B 中,我用 switch 语句替换了 if let。但是我正在使用 where 并检查 nil,然后从这一点开始强制展开我的值。

我想知道是否有类似于 Loop C 的方法来解包值,以便我可以像在 Loop 中那样使用那些新的非可选变量A.

import Foundation

// Setup
protocol MyObjects {}

struct MyTopic: MyObjects {
    let name: String?
}

struct MyQuestion: MyObjects {
    let text: String?
    let topic: String?
}

let topicOrQuestions: [MyObjects] = [
    MyQuestion(text: "questionA", topic: "topicA"),
    MyTopic(name: "topicA"),
    MyTopic(name: "topicB"),
    MyTopic(name: nil)
]

// Loop A:
for topicOrQuestion in topicOrQuestions {
    if let name = (topicOrQuestion as? MyTopic)?.name {
        print(name)
    } else if let text = (topicOrQuestion as? MyQuestion)?.text, let topic = (topicOrQuestion as? MyQuestion)?.topic {
        print(text, topic)
    } else {
        print("Error: wrong type or nil value")
    }
}

// Loop B:
for topicOrQuestion in topicOrQuestions {

    switch topicOrQuestion {
    case let topic as MyTopic where topic.name != nil:
        print(topic.name!)
    case let question as MyQuestion where question.text != nil && question.topic != nil:
        print(question.text!, question.topic!)
    default:
        print("Error: wrong type or nil value")
    }
}

// Loop C (doesn't work):
for topicOrQuestion in topicOrQuestions {

    switch topicOrQuestion {
    case let name as MyTopic.name: // <-- Is it possible to unwrap the values here directly similar to the if let's in Loop A?
        print(name)
    case let text as MyQuestion.text, let topic as MyQuestion.topic:
        print(text, topic)
    default:
        print("Error: wrong type or nil value")
    }
}

/*
Current output from Loop A and B and expected output from Loop C:

questionA topicA
topicA
topicB
Error: wrong type or nil value
*/

我不认为你能在 case 中转换和解包,但有了这个扩展至少你的 Loop A 会不那么冗长:

extension MyObjects {
    subscript<T: MyObjects, V>(topicOrQuestion keypath: (T) -> V) -> V? {
        get {
            guard let root = self as? T else { return nil }
            return keypath(root)
        }
    }
}

你也可以写这个循环:

for topicOrQuestion in topicOrQuestions {

    if let topicName = topicOrQuestion[topicOrQuestion: \MyTopic.name] as? String  {
        print(topicName)
    }
    if let questionText = topicOrQuestion[topicOrQuestion: \MyQuestion.text] as? String ,
       let questionTopic = topicOrQuestion[topicOrQuestion: \MyQuestion.topic] as? String {
        print(questionText, questionTopic)
    }
}
// questionA topicA
// topicA
// topicB