我想测试两个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那样。


当前回答

与接受的答案相比,“优势”是,在“main”开关语句中没有“默认”大小写,所以如果你用其他大小写扩展枚举,编译器将强制你更新其余代码。

enum SimpleToken: Equatable {
    case Name(String)
    case Number(Int)
}
extension SimpleToken {
    func isEqual(st: SimpleToken)->Bool {
        switch self {
        case .Name(let v1):
            switch st {
            case .Name(let v2): return v1 == v2
            default: return false
            }
        case .Number(let i1):
            switch st {
            case .Number(let i2): return i1 == i2
            default: return false
            }
        }
    }
}


func ==(lhs: SimpleToken, rhs: SimpleToken)->Bool {
    return lhs.isEqual(rhs)
}

let t1 = SimpleToken.Number(1)
let t2 = SimpleToken.Number(2)
let t3 = SimpleToken.Name("a")
let t4 = SimpleToken.Name("b")

t1 == t1  // true
t1 == t2  // false
t3 == t3  // true
t3 == t4  // false
t1 == t3  // false

其他回答

我在单元测试代码中使用这个简单的解决方法:

extension SimpleToken: Equatable {}
func ==(lhs: SimpleToken, rhs: SimpleToken) -> Bool {
    return String(stringInterpolationSegment: lhs) == String(stringInterpolationSegment: rhs)
}

它使用字符串插值来执行比较。我不建议在生产代码中使用它,但是它很简洁,并且可以用于单元测试。

从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
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
    }
}

这是另一个选择。除了使用if大小写语法避免了嵌套的switch语句外,它主要与其他的相同。我认为这使它更具可读性(/可接受性),并具有完全避免默认情况的优点。

enum SimpleToken: Equatable {
    case Name(String)
    case Number(Int)
}
extension SimpleToken {
    func isEqual(st: SimpleToken)->Bool {
        switch self {
        case .Name(let v1): 
            if case .Name(let v2) = st where v1 == v2 { return true }
        case .Number(let i1): 
            if case .Number(let i2) = st where i1 == i2 { return true }
        }
        return false
    }
}

func ==(lhs: SimpleToken, rhs: SimpleToken)->Bool {
    return lhs.isEqual(rhs)
}

let t1 = SimpleToken.Number(1)
let t2 = SimpleToken.Number(2)
let t3 = SimpleToken.Name("a")
let t4 = SimpleToken.Name("b")

t1 == t1  // true
t1 == t2  // false
t3 == t3  // true
t3 == t4  // false
t1 == t3  // false

扩展mbpro的答案,下面是我如何使用该方法在一些边缘情况下检查swift枚举与相关值的相等性。

当然,你也可以使用switch语句,但有时最好只检查一行中的一个值。你可以这样做:

// NOTE: there's only 1 equal (`=`) sign! Not the 2 (`==`) that you're used to for the equality operator
// 2nd NOTE: Your variable must come 2nd in the clause

if case .yourEnumCase(associatedValueIfNeeded) = yourEnumVariable {
  // success
}

如果你想比较同一个If子句中的两个条件,你需要使用逗号而不是&&操作符:

if someOtherCondition, case .yourEnumCase = yourEnumVariable {
  // success
}