简体   繁体   English

比较忽略关联值的Swift Enum类型 - 通用实现

[英]Compare Swift Enum types ignoring associated values - generic implementation

I have several different enums in my project, that conform to the same protocol. 我的项目中有几个不同的枚举,符合相同的协议。 The compareEnumType method from the protocol compares enum cases ignoring associated values. 协议中的compareEnumType方法比较忽略关联值的枚举情况。 Here is my code from playground: 这是我在游乐场的代码:

protocol EquatableEnumType {
    static func compareEnumType(lhs: Self, rhs: Self) -> Bool
}

enum MyEnum: EquatableEnumType {
    case A(Int)
    case B

    static func compareEnumType(lhs: MyEnum, rhs: MyEnum) -> Bool {
        switch (lhs, rhs) {
        case (.A, .A): return true
        case (.B, .B): return true
        default: return false
        }
    }
}

enum MyEnum2: EquatableEnumType {
    case X(String)
    case Y

    static func compareEnumType(lhs: MyEnum2, rhs: MyEnum2) -> Bool {
        switch (lhs, rhs) {
        case (.X, .X): return true
        case (.Y, .Y): return true
        default: return false
        }
    }
}

let a = MyEnum.A(5)
let a1 = MyEnum.A(3)
if MyEnum.compareEnumType(lhs: a, rhs: a1) {
    print("equal") // -> true, prints "equal"
}

let x = MyEnum2.X("table")
let x1 = MyEnum2.X("chair")
if MyEnum2.compareEnumType(lhs: x, rhs: x1) {
    print("equal2") // -> true, prints "equal2"
}

In my real project I have more than 2 enums, and for each of them I have to have similar implementation of compareEnumType function. 在我的真实项目中,我有两个以上的枚举,对于每个枚举,我必须有compareEnumType函数的类似实现。

The question is: is it possible to have a generic implementation of compareEnumType which would work for all enums conforming to EquatableEnumType protocol? 问题是:是否可以使用compareEnumType的通用实现,它适用于符合EquatableEnumType协议的所有枚举?

I tried to write a default implementation in protocol extension like this: 我尝试在协议扩展中编写一个默认实现,如下所示:

extension EquatableEnumType {
    static func compareEnumType(lhs: Self, rhs: Self) -> Bool {
        // how to implement???
    }
}

But I'm stuck with implementation. 但我坚持实施。 I don't see a way to access a value contained in lhs and rhs . 我没有看到访问lhsrhs包含的值的方法。 Could anyone help me? 谁能帮助我?

I don't think that you can autogenerate this so here is a way using extensions. 我不认为你可以自动生成这个,所以这是一种使用扩展的方式。 I suggest to create a new enum CompareEnumMethod which tells if you want to compare the associated values or only the type . 我建议创建一个新的enum CompareEnumMethod ,它告诉您是要比较关联的values还是仅比较type Create a new function compareEnum in your protocol that has this enum as a parameter. 在您的协议中创建一个新函数compareEnum ,该函数将此枚举作为参数。 Then, you can create an extension for ==(lhs:,rhs:) and say that it uses .value and for compareEnumType you use .type . 然后,您可以为==(lhs:,rhs:)创建一个扩展名,并说它使用.value而对于compareEnumType ,则使用.type Only the compareEnum method has to be implemented in each enum now. 现在只需要在每个枚举中实现compareEnum方法。

enum CompareEnumMethod {
    case type, value
}

protocol EquatableEnumType: Equatable {
    static func compareEnumType(lhs: Self, rhs: Self) -> Bool
    static func compareEnum(lhs: Self, rhs: Self, method: CompareEnumMethod) -> Bool
}

extension EquatableEnumType {
    static func compareEnumType(lhs: Self, rhs: Self) -> Bool {
        return Self.compareEnum(lhs: lhs, rhs: rhs, method: .type)
    }

    static func ==(lhs: Self, rhs: Self) -> Bool {
        return Self.compareEnum(lhs: lhs, rhs: rhs, method: .value)
    }
}

enum MyEnum: EquatableEnumType {
    case A(Int)
    case B

    static func compareEnum(lhs: MyEnum, rhs: MyEnum, method: CompareEnumMethod) -> Bool {
        switch (lhs, rhs, method) {
        case let (.A(lhsA), .A(rhsA), .value):
            return lhsA == rhsA
        case (.A, .A, .type),
             (.B, .B, _):
            return true
        default:
            return false
        }
    }
}

let a0 = MyEnum.A(5)
let a1 = MyEnum.A(3)
let b0 = MyEnum.B
print(MyEnum.compareEnumType(lhs: a0, rhs: a1)) //true
print(a0 == a1) //false
print(MyEnum.compareEnumType(lhs: a0, rhs: b0)) //false

Easy! 简单! I would use an instance method, but you can rewrite it to a class function, if you really need it to be static. 我会使用一个实例方法,但你可以将它重写为类函数,如果你真的需要它是静态的。

extension EquatableEnumCase {
    func isSameCase(as other: Self) -> Bool {
        let mirrorSelf = Mirror(reflecting: self)
        let mirrorOther = Mirror(reflecting: other)
        if let caseSelf = mirrorSelf.children.first?.label, let caseOther = mirrorOther.children.first?.label {
            return (caseSelf == caseOther) //Avoid nil comparation, because (nil == nil) returns true
        } else { return false}
    }
} 


enum MyEnum1: EquatableEnumCase {
    case A(Int)
    case B
}

enum MyEnum2: EquatableEnumCase {
    case X(String)
    case Y
}

let a = MyEnum1.A(5)
let a1 = MyEnum1.A(3)
if a.isSameCase(as: a1) {
    print("equal") // -> true, prints "equal1"
}

let x = MyEnum2.X("table")
let x1 = MyEnum2.X("chair")

if x.isSameCase(as: x1) {
    print("equal2") // -> true, prints "equal2"
}

let y = MyEnum2.Y
print(x.isSameCase(as: y) ? "equal3": "not equal3") // -> false, "not equal3"

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM