我尝试在以下场景中编写更少的代码:

我有这个 Queryable 协议(protocol)和一个 Parameter 枚举:

protocol Queryable {
    var urlQuery: URLQueryItem { get }
}

enum PaginationParameter: Queryable {
    case page(Int)
    case pageSize(Int)

    var queryItem: URLQueryItem {
        switch self {
        case .page(let page):
            return URLQueryItem(name: "page", value: page.description)
        case .pageSize(let pageSize):
            return URLQueryItem(name: "page_size", value: pageSize.description)
        }
    }
}

以及提供一些默认情况和由泛型类型定义的一些特定情况的枚举:

enum Parameter<P: Queryable> {
    case pagination(PaginationParameter)
    case specific(P)
}

示例用法

enum BookParameters: Queryable {
    case search(String)
    case id(Int)

    var urlQuery: URLQueryItem {
        switch self {
        case .search(let string):
            return URLQueryItem(name: "search", value: string)
        case .id(let id):
            return URLQueryItem(name: "id", value: id.description)
        }
    }
}

let parameters: [Parameter<BookParameters>] = [
    .pagination(.pageSize(10)),
    .specific(.id(123))
]

现在我需要通过 .pagination.specific 两种情况获取 url 查询项。

let queryItems = parameters.map({
    switch $0 {
    case .pagination(let param):
        return param.queryItem
    case .specific(let param):
        return param.queryItem
    }
})

有一种方法来处理组合的嵌套案例会很好,因为它们符合相同的协议(protocol)。这不起作用,因为我必须通过父案例去嵌套案例:

一个小的改进是将 switch 语句埋在参数枚举的扩展中,并让它也符合 Queryable 协议(protocol):

extension Parameters: Queryable {

    let queryItem: URLQueryItem {
        switch self {
        case .pagination(let param):
            return param.queryItem
        case .specific(let param):
            return param.queryItem
        }
    }
}

这导致了一个类轮,但我只是将我的问题转移到了不同​​的地方。

let queryItems = parameters.map({ $0.queryItem })

最佳答案

由于您使用的是具有关联值的嵌套枚举,因此我真的没有看到避免在顶级 Parameter 枚举上使用此额外开关的方法。就我而言,Swift 没有为我们提供一个工具来处理案例,我们可以将所有具有“相同”关联值类型的案例转换为单个案例。您可以做的是重新考虑 Parameter 类型的存在,因为它似乎并没有真正有用,因为您仍然需要将其称为 Parameter<BookParameters>Parameter<SomeOtherTypeThatConformsToQueryable>
我个人会跳过顶级枚举,直接将 parameters 属性类型称为 [Queryable]

var parameters: [Queryable] = [
    PaginationParameter.pageSize(10),
    BookParameters.id(123)
]
使事情变得更简单,更容易推理。此外,现在还有一种方法可以添加其他类型的其他案例,而您的初始解决方案无法做到这一点。
enum SomeOtherTypeThatConformsToQueryable: Queryable {
    case aVeryNiceCase(Int)
}

parameters.append(SomeOtherTypeThatConformsToQueryable.aVeryNiceCase(0))
// Appending this to array of type `[Parameter<BookParameters>]`, would not be
// possible without explicitly adding new case to the `Parameter` enumeration
此外,如果您发现自己经常调用 map { $0.queryItem },您可以提供 Array 的扩展,其中 ElementQueryable 的类型
extension Array where Element == Queryable {
    var queryItems: [URLQueryItem] { return map { $0.queryItem } }
}

// And now you can simply call
let queryItems = parameters.queryItems

关于Swift:具有符合相同协议(protocol)的嵌套枚举案例的更少代码,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/55849372/

10-11 17:13