首页 > 解决方案 > 如何在 switch 语句中(同时)键入匹配和解包可选值?

问题描述

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

在以下操场代码中,有三个循环:

循环 A用于if let匹配类型并同时解开可选值。

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

我想知道是否有类似于循环 C的方法来解包这些值,以便我可以像在循环 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
*/

标签: swiftswitch-statementpattern-matching

解决方案


我不认为你将能够在一个案例中进行投射和解包,但有了这个扩展,至少你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


推荐阅读