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

问题描述

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

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

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

循环 B 中,我将 if let 替换为 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
*/

解决方法

我认为您无法在案例中进行转换和解包,但使用此扩展程序至少您的 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