我想测试两个Swift枚举值的相等性.例如:
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一样.
我在单元测试代码中使用这个简单的解决方法:
extension SimpleToken: Equatable {} func ==(lhs: SimpleToken, rhs: SimpleToken) -> Bool { return String(stringInterpolationSegment: lhs) == String(stringInterpolationSegment: rhs) }
它使用字符串插值来执行比较.我不推荐它用于生产代码,但它简洁并且可以完成单元测试.
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 } }
正如其他人所说,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语句进行嵌套开关.
另一种选择是比较案例的字符串表示:
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
实施Equatable
是一个矫枉过正的恕我直言.想象一下,你有许多案例和许多不同参数的复杂和大枚举.这些参数也都必须Equatable
实现.此外,谁说您在全有或全无的基础上比较枚举案例?如果您正在测试值并且仅存在一个特定的枚举参数,那该怎么办?我强烈建议简单的方法,如:
if case .NotRecognized = error { // Success } else { XCTFail("wrong error") }
......或在参数评估的情况下:
if case .Unauthorized401(_, let response, _) = networkError { XCTAssertEqual(response.statusCode, 401) } else { XCTFail("Unauthorized401 was expected") }
在此处查找更详细的说明:https://mdcdeveloper.wordpress.com/2016/12/16/unit-testing-swift-enums/
这是另一种选择.除了通过使用if case
语法避免嵌套的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
对于枚举,结构似乎没有编译器生成的相等运算符.
"例如,如果您创建自己的类或结构来表示复杂的数据模型,则该类或结构的"等于"的含义不是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上的 "等效运营商"