我想测试两个Swift enum值的相等性。例如:

enum SimpleToken {
    case Name(String)
    case Number(Int)
}
let t1 = SimpleToken.Number(123)
let t2 = SimpleToken.Number(123)
XCTAssert(t1 == t2)

但是,编译器不会编译等式表达式:

error: could not find an overload for '==' that accepts the supplied arguments
    XCTAssert(t1 == t2)
    ^~~~~~~~~~~~~~~~~~~

我需要自己定义重载的相等运算符吗?我希望Swift编译器能自动处理它,就像Scala和Ocaml那样。


当前回答

另一种选择是比较大小写的字符串表示:

XCTAssert(String(t1) == String(t2))

例如:

let t1 = SimpleToken.Number(123) // the string representation is "Number(123)"
let t2 = SimpleToken.Number(123)
let t3 = SimpleToken.Name("bob") // the string representation is "Name(\"bob\")"

String(t1) == String(t2) //true
String(t1) == String(t3) //false

其他回答

对于枚举和结构,似乎没有编译器生成的相等操作符。

例如,如果你创建了自己的类或结构来表示一个复杂的数据模型,那么这个类或结构的“等于”的含义就不是Swift能帮你猜出来的。”[1]

要实现相等性比较,可以这样写:

@infix func ==(a:SimpleToken, b:SimpleToken) -> Bool {
    switch(a) {

    case let .Name(sa):
        switch(b) {
        case let .Name(sb): return sa == sb
        default: return false
        }

    case let .Number(na):
        switch(b) {
        case let .Number(nb): return na == nb
        default: return false
        }
    }
}

[1]参见https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-XID_43的“等价运算符”

enum MyEnum {
    case none
    case simple(text: String)
    case advanced(x: Int, y: Int)
}

func ==(lhs: MyEnum, rhs: MyEnum) -> Bool {
    switch (lhs, rhs) {
    case (.none, .none):
        return true
    case let (.simple(v0), .simple(v1)):
        return v0 == v1
    case let (.advanced(x0, y0), .advanced(x1, y1)):
        return x0 == x1 && y0 == y1
    default:
        return false
    }
}

斯威夫特4.1 +

正如@jedwidz指出的那样,从Swift 4.1开始(由于SE-0185, Swift还支持为枚举合成相关值的Equatable和Hashable。

因此,如果您使用的是Swift 4.1或更新版本,下面将自动合成必要的方法,以便XCTAssert(t1 == t2)工作。关键是将Equatable协议添加到枚举中。

enum SimpleToken: Equatable {
    case Name(String)
    case Number(Int)
}
let t1 = SimpleToken.Number(123)
let t2 = SimpleToken.Number(123)

Swift 4.1之前

正如其他人所注意到的,Swift不会自动合成必要的相等操作符。不过,让我提出一个更清晰的实现:

enum SimpleToken: Equatable {
    case Name(String)
    case Number(Int)
}

public func ==(lhs: SimpleToken, rhs: SimpleToken) -> Bool {
    switch (lhs, rhs) {
    case let (.Name(a),   .Name(b)),
         let (.Number(a), .Number(b)):
      return a == b
    default:
      return false
    }
}

这远非理想——有很多重复——但至少你不需要在if语句里面做嵌套开关。

另一种方法使用if大小写加逗号,在Swift 3中有效:

enum {
  case kindOne(String)
  case kindTwo(NSManagedObjectID)
  case kindThree(Int)

  static func ==(lhs: MyEnumType, rhs: MyEnumType) -> Bool {
    if case .kindOne(let l) = lhs,
        case .kindOne(let r) = rhs {
        return l == r
    }
    if case .kindTwo(let l) = lhs,
        case .kindTwo(let r) = rhs {
        return l == r
    }
    if case .kindThree(let l) = lhs,
        case .kindThree(let r) = rhs {
        return l == r
    }
    return false
  }
}

我是这样写我的项目的。但我不记得我是怎么想到的。(我刚刚谷歌了一下,但没有看到这样的用法。)任何意见都将不胜感激。

从Swift 4.1中,只需将Equatable协议添加到您的枚举中,并使用XCTAssert或XCTAssertEqual:

enum SimpleToken : Equatable {
    case Name(String)
    case Number(Int)
}
let t1 = SimpleToken.Number(123)
let t2 = SimpleToken.Number(123)
XCTAssertEqual(t1, t2) // OK