【问题标题】:How to reduce Swift enum conversion code如何减少 Swift 枚举转换代码
【发布时间】:2018-01-01 22:46:05
【问题描述】:

我有各种各样的枚举,如下所示。

enum PaperOrientation : Int { case portrait, landscape }
enum MetricType : Int { case inches, metric }

我创建了 Int 类型的枚举,以便实例的值可以使用 CoreData 保存为数字。

当从 CoreData 检索值以在程序中使用时,我最终得到了非常相似的转换例程,如下所示。

通常,我想要一些默认值 - 例如,它是最新版本程序的新枚举,并且该变量的值实际上可能没有保存在 CoreData 中。例如,为程序的第二个版本添加了 MetricType。检索在 rev 1 中创建的论文将不会保存度量值。对于 nil 值,我想使用论文最初假定的默认值。

class ConversionRoutine {

    class func orientationFor(_ num: NSNumber?) -> PaperOrientation {
        if let iVal = num?.intValue {
            if let val = PaperOrientation(rawValue: iVal) {
                return val
            }
        }
        return PaperOrientation(rawValue: 0)!
    }

    class func metricTypeFor(_ num: NSNumber?) -> MetricType {
        if let iVal = num?.intValue {
            if let val = MetricType(rawValue: iVal) {
                return val
            }
        }
        return MetricType(rawValue: 0)!
    }
}

有没有办法减少冗余?

我在下面介绍了一种效果很好的方法。但欢迎进行更多改进或改进。

【问题讨论】:

  • return PaperOrientation(rawValue: num?.intValue ?? 0) ?? .portrait
  • 或者不要将您的 num 声明为可选的 NSNumber,只需为其分配一个默认值 func orientationFor(_ num: NSNumber = 0)return PaperOrientation(rawValue: num.intValue) ?? .portrait
  • 可以清理一些转换例程。我喜欢第一个回复中的单线。但主要是,我对减少整体代码量很感兴趣——到目前为止,我发现在下面的答案中可以通过协议扩展来完成。此外,在这种特殊情况下,我关心来自 CoreData 的数据,其中数据是可选的。对于我的一个应用程序,我可能有 30 个(并且还在不断增加)从 CoreData 转换数据的案例。到目前为止,创建 IntDefaultable 协议确实简化了新案例的定义和使用。但仍在寻找潜在的改进。

标签: swift generics core-data enums


【解决方案1】:

下面的 Swift 4 示例使用基于 RawRepresentable 的 Defaultable 协议。第一步是创建一个可以在初始化失败时使用的 defaultValue。请注意,Defaultable 协议不限于 Int 枚举。字符串枚举也可以使用它。

protocol Defaultable : RawRepresentable {
    static var defaultValue : Self { get }
}

protocol IntDefaultable : Defaultable where RawValue == Int {
}

extension IntDefaultable {
    static func value(for intValue : Int) -> Self {
        return Self.init(rawValue: intValue) ?? Self.defaultValue
    }

    static func value(for num : NSNumber?) -> Self {
        if let iVal = num?.intValue {
            return self.value(for: iVal)
        }
        return Self.defaultValue
    }
}

定义 Defaultable 协议后,我可以创建一个用于 Int 枚举的 IntDefaultable 协议。

在 IntDefaultable 的扩展中,我可以创建通用代码来处理转换。首先,我创建了一个采用 Int 的函数。然后我创建一个函数,它接受一个 NSNumber 可选。

接下来,看看其中一个枚举是如何构建的:

enum MetricType : Int, Codable, IntDefaultable { case inches, metric
    static var defaultValue: MetricType = .inches
}

我还决定声明枚举 Codable,这可能会有用。当我添加 IntDefaultable 协议时,使用代码完成添加 defaultValue 代码行变得相当容易 - 转到新行并输入“def”-tab,然后输入“=.”,然后从弹出窗口。请注意,我通常想选择第一个枚举值,但默认值可以是任何一个。

最后一件事是调用转换例程从 CoreData 获取值

let units = MetricType.value(for: self.metricType)  // where self.metricType is the NSManagedObject member.

【讨论】:

    【解决方案2】:

    您可以在enum 中添加初始化程序。

    enum PaperOrientation : Int { 
        case portrait, landscape
    
        init(number: NSNumber) {
            self = PaperOrientation(rawValue: number.intValue) ?? .portrait
        }
    }
    

    【讨论】:

    • @LeoDabus 抱歉,我忘记将类型更改为NSNumber。在这种情况下,这将是有意义的。
    • 这里的问题是 CoreData 将 Int 存储到 NSNumber 并且默认初始化程序不能在默认情况下使用 NSNumber。我相信这是最好的方法。
    • @LeoDabus 为什么我要使用 nil 合并,即使我们需要一个默认值?使用default case 会简单很多。
    猜你喜欢
    • 2021-05-07
    • 1970-01-01
    • 2020-06-27
    • 1970-01-01
    • 1970-01-01
    • 2019-06-27
    • 1970-01-01
    • 1970-01-01
    • 2015-02-14
    相关资源
    最近更新 更多