Swift 枚举顺序与比较

Swift Enumeration order and comparison

关于如何按定义顺序比较 Swift 中的枚举的 finding/understanding 文档,我遇到了麻烦。特别是当我创建一个枚举时,例如

enum EnumType {
    case First,  Second, Third
}

Swift 不允许我直接按顺序比较枚举,例如

let type1 = EnumType.First
let type2 = EnumType.Second
if type1 < type2 {println("good")} // error

它会生成编译错误“无法使用 {EnumType, EnumType} 类型的参数列表调用 '<'。所以我找到的唯一解决方案是编写自己的比较运算符作为重载,例如

enum EnumType : Int {
    case First = 0, Second, Third
}

func <(a: EnumType, b: EnumType) -> Bool {
    return a.rawValue < b.rawValue
}

let type1 = EnumType.First
let type2 = EnumType.Second
if type1 < type2 {println("good")} // Returns "good"

这对于在我的应用程序中有很多用途和价值的 "heavy weight" 枚举来说一切都很好,但是重载我可能想要使用的所有运算符对于我认为的“轻量级”枚举来说似乎过于繁重可能会即时定义以对单个小模块的某些常量进行排序。

有没有办法在不为我在项目中定义的每个枚举类型编写大量样板重载代码的情况下做到这一点?更好的是,我是否缺少让 Swift 自动为没有关联类型的简单枚举提供比较运算符的东西,即。是无类型的还是类型为 Int 的? Swift 知道如何比较 Int,为什么不能比较 enum Int?

只要你给你的枚举一个底层类型,它就会符合协议RawRepresentable

这意味着您可以为任何可原始表示的类型编写通用比较运算符,并且具有可比较的原始类型,如下所示:

func <<T: RawRepresentable where T.RawValue: Comparable>(a: T, b: T) -> Bool {
    return a.rawValue < b.rawValue
}

这意味着您的枚举将自动具有一个 < 运算符:

enum E: Int {  // this would work with Double and String also
    // btw, no need to give a seed value of 0,
    // that happens automatically for Ints
    case A, B, C, D, E
}

E.A < E.C  // returns true

您仍然需要做的唯一样板文件是将您的枚举标记为 Comparable,以防您想将它与需要以下条件的通用算法一起使用:

extension E: Comparable { }
// (no need for anything else - requirements are already fulfilled)

let a: [E] = [.C, .E, .A]
let b = sorted(a)
// b will now be [.A, .C, .E]

使其符合 Comparable 也会自动为其提供 <=>>= 运算符(由标准库提供)。

这在某种程度上与OP自己提出的答案相同。它确实涉及到您想要比较的每个枚举的一些样板代码,但我更喜欢这样做,而不是拥有一些提供可与所有枚举进行比较的外部魔法函数。如果您从一个程序快速复制并粘贴到另一个程序,然后枚举不起作用并且您不记得原因,这可能会导致问题。

public enum LogLevel: Int, Comparable {
    case verbose
    case debug
    case info
    case warning
    case error
    case severe

    // Implement Comparable
    public static func < (a: LogLevel, b: LogLevel) -> Bool {
        return a.rawValue < b.rawValue
    }
}

编辑:

这是对@JasonMoore 评论的回应。

Comparable 不需要==。这是 Equatable 所要求的,Swift 标准库自动为大多数类型的枚举提供 Equatable。

http://www.jessesquires.com/blog/swift-enumerations-and-equatable/

至于 >、<= 和 >=,Apple 文档说它们是 Comparable 所必需的,但提供了默认实现(我假设基于 == 和 < 的使用)。

https://developer.apple.com/documentation/swift/comparable

这是我在 IBM Swift 沙箱中 运行 的一些代码 - 它可以根据上述定义编译并运行良好。

let a : LogLevel = LogLevel.verbose
let b : LogLevel = LogLevel.verbose
let c : LogLevel = LogLevel.warning

print(a == b)  // prints true
print(a > c)  // prints false
print(a <= c)  // prints true

在 Swift 的较新版本中,您可以创建一个协议来实现此目的,而无需通用全局变量。这也使您能够选择影响的枚举。

/// Allows a raw enum type to be compared by the underlying comparable RawValue
public protocol RawComparable : Comparable where Self : RawRepresentable, RawValue: Comparable {
}

extension RawComparable {
    public static func < (lhs: Self, rhs: Self) -> Bool {
        return lhs.rawValue < rhs.rawValue
    }
}

要使用它就像将 RawComparable 协议添加到枚举类型一样简单:

enum EnumType : Int, RawComparable {
    case First = 0, Second, Third
}

Swift 5.3 开始,可以按照 OP 的要求比较枚举。

它的工作原理如下(来自提案):

enum Brightness: Comparable {
    case low
    case medium
    case high
}

let expectedBrightness = Brightness.low
let actualBrightness = Brightness.high

if actualBrightness > expectedBrightness {
    // Do something
}

更多信息和示例here