首页 > 解决方案 > 我们能否像在 Swift 中使用 Equatable 协议覆盖 operator == 一样覆盖 switch case 行为

问题描述

在开关情况下比较枚举时未调用 Equatable 协议,它如何比较值?

我在 Enum 下面实现了,它与 If 条件完美配合,但在 Switch case 中失败,switch case 如何比较 case 值?它在内部不使用 == 吗?

这是我的代码

    import Foundation

public enum AMSelectionStyle{
    case single
    case multiple
    case any
}

public enum AMCalenderMode{
    case date
    case time
    case dateTime
    case any
    
}

public enum AMNumberType:String{
    case natural = "N" //(also called positive integers, counting numbers, or natural numbers); They are the numbers {1, 2, 3, 4, 5, …}
    case whole = "W" //This is the set of  natural numbers, plus zero, i.e., {0, 1, 2, 3, 4, 5, …}.
    case integer = "Z" // This is the set of all whole numbers plus all the negatives (or opposites) of the natural numbers, i.e., {… , ⁻2, ⁻1, 0, 1, 2, …}
    case rational = "Q" //This is all the fractions where the top and bottom numbers are integers; e.g., 1/2, 3/4, 7/2, ⁻4/3, 4/1 [Note: The denominator cannot be 0, but the numerator can be].
    case real = "R" //(also called measuring numbers or measurement numbers). This includes all numbers that can be written as a decimal. This includes fractions written in decimal form e.g., 0.5, 0.75 2.35, ⁻0.073, 0.3333, or 2.142857. It also includes all the irrational numbers such as π, √2 etc. Every real number corresponds to a point on the number line.
    case all = "" // all of the above
}
public enum AMInputType:Equatable {
    case list(AMSelectionStyle)
    case number(AMNumberType)
    case calender(AMCalenderMode)
    case freeText
    case boolian
    case picklist
    
   public static func ==(lhs: AMInputType, rhs: AMInputType) -> Bool {
        switch (lhs, rhs) {
        case (let .list(a1), let .list(a2)):
            if a1 == .any || a2 == .any {
                return true
            }else if a1 == .single && a2 == .single {
                return true
            }else if a1 == .multiple && a2 == .multiple {
                return true
            }
            return a1 == a2
        case (let .number(a1), let .number(a2)):
            if a1 == .all || a2 == .all {
                return true
            }else if a1 == .natural && a2 == .natural {
                return true
            }else if a1 == .whole && a2 == .whole {
                return true
            }else if a1 == .integer && a2 == .integer {
                return true
            }else if a1 == .rational && a2 == .rational {
                return true
            }else if a1 == .real && a2 == .real {
                return true
            }
            return a1 == a2
        case  (let .calender(a1), let .calender(a2)):
            if a1 == .any || a2 == .any {
                return true
            }else if a1 == .date && a2 == .date {
                return true
            }else if a1 == .time && a2 == .time {
                return true
            }else if a1 == .dateTime && a2 == .dateTime {
                return true
            }
            return a1 == a2
        case (.freeText, .freeText):
            return true
        case (.boolian, .boolian):
            return true
        case (.picklist, .picklist):
            return true
        default:
            return false
        }
    }

}

func compareEnum(inputType:AMInputType)->Bool{
    switch inputType {
    case .number(.all):
        print("Switch case `number` check Successful")
        return true
    case .calender(.any):
        print("Switch case `calender` check Successful")
        return true
    case .list(.any):
        print("Switch case `list` check Successful")
        return true
    default:
        print("Switch case check Failed")
        return false
    }
}

当枚举参数值与案例编号不同时比较失败

compareEnum(inputType: .number(.real))

当枚举参数值与案例编号相同时,比较成功。

compareEnum(inputType: .number(.all))

如果条件成功,则案例编号的不同枚举参数。

if AMInputType.number(.all) == AMInputType.number(.real) {
    print("If Condition `number` check Successful")
}

编辑 1 - 根据@Tarun Tyagi 的建议转换枚举以实现 Equitable

问题仍然存在。在 Switch 案例中未调用公平协议

    public enum AMSelectionStyle:Int, Equatable{
    case single
    case multiple
    case any
    
    public static func ==(lhs: AMSelectionStyle, rhs: AMSelectionStyle) -> Bool {
        switch (lhs, rhs) {

        case (.single, .single):
            return true
        case (.single, .multiple):
            return false
        case (.single, .any):
            return true

            
        case (.multiple, .multiple):
            return true
        case (.multiple, .single):
            return false
        case (.multiple, .any):
            return true

            
        case (.any, .any):
            return true
        case (.any, .single):
            return true
        case (.any, .multiple):
            return true
            
        }
    }
}

public enum AMCalenderMode:Int, Equatable{
    case date
    case time
    case dateTime
    case any
    
    public static func ==(lhs: AMCalenderMode, rhs: AMCalenderMode) -> Bool {
        
        switch (lhs, rhs) {
        
        case (.date, .date):
            return true
        case (.date, .time):
            return false
        case (.date, .dateTime):
            return false
        case (.date, .any):
            return true

            
        case (.time, .time):
            return true
        case (.time, .date):
            return false
        case (.time, .dateTime):
            return false
        case (.time, .any):
            return true


        case (.dateTime, .dateTime):
            return true
        case (.dateTime, .date):
            return false
        case (.dateTime, .time):
            return false
        case (.dateTime, .any):
            return true
            
        case (.any, .any):
            return true
        case (.any, .date):
            return true
        case (.any, .time):
            return true
        case (.any, .dateTime):
            return true
            
        }
    }
    
}

public enum AMNumberType:String, Equatable{
    case natural = "N" //(also called positive integers, counting numbers, or natural numbers); They are the numbers {1, 2, 3, 4, 5, …}
    case whole = "W" //This is the set of  natural numbers, plus zero, i.e., {0, 1, 2, 3, 4, 5, …}.
    case integer = "Z" // This is the set of all whole numbers plus all the negatives (or opposites) of the natural numbers, i.e., {… , ⁻2, ⁻1, 0, 1, 2, …}
    case rational = "Q" //This is all the fractions where the top and bottom numbers are integers; e.g., 1/2, 3/4, 7/2, ⁻4/3, 4/1 [Note: The denominator cannot be 0, but the numerator can be].
    case real = "R" //(also called measuring numbers or measurement numbers). This includes all numbers that can be written as a decimal. This includes fractions written in decimal form e.g., 0.5, 0.75 2.35, ⁻0.073, 0.3333, or 2.142857. It also includes all the irrational numbers such as π, √2 etc. Every real number corresponds to a point on the number line.
    case all = "" // all of the above
    
    public static func ==(lhs: AMNumberType, rhs: AMNumberType) -> Bool {
        
        switch (lhs, rhs) {
        
        case (.natural, .natural):
            return true
        case (.natural, .whole):
            return false
        case (.natural, .integer):
            return false
        case (.natural, .rational):
            return false
        case (.natural, .real):
            return false
        case (.natural, .all):
            return true
            
        case (.whole, .whole):
            return true
        case (.whole, .natural):
            return false
        case (.whole, .integer):
            return false
        case (.whole, .rational):
            return false
        case (.whole, .real):
            return false
        case (.whole, .all):
            return true
          
        case (.integer, .integer):
            return true
        case (.integer, .natural):
            return false
        case (.integer, .whole):
            return false
        case (.integer, .rational):
            return false
        case (.integer, .real):
            return false
        case (.integer, .all):
            return true
            
        case (.rational, .rational):
            return true
        case (.rational, .natural):
            return false
        case (.rational, .whole):
            return false
        case (.rational, .integer):
            return false
        case (.rational, .real):
            return false
        case (.rational, .all):
            return true
            
        case (.real, .real):
            return true
        case (.real, .natural):
            return false
        case (.real, .whole):
            return false
        case (.real, .integer):
            return false
        case (.real, .rational):
            return false
        case (.real, .all):
            return true
            
        case (.all, .all):
            return true
        case (.all, .natural):
            return true
        case (.all, .whole):
            return true
        case (.all, .integer):
            return true
        case (.all, .rational):
            return true
        case (.all, .real):
            return true
        }
    }
}

标签: iosswiftswitch-statementoperator-overloadingoperator-keyword

解决方案


只要所有案例(及其相关值)都Equatable符合.enumEquatable

所以默认情况下你会得到一个正确的实现,你只需要Equatable在所有必要的地方做标记。

import Foundation

public enum AMSelectionStyle: Int, Equatable {
    case single
    case multiple
    case any
}
public enum AMCalenderMode: Int, Equatable {
    case date
    case time
    case dateTime
    case any
}
public enum AMNumberType: String, Equatable {
    case natural = "N"
    case whole = "W"
    case integer = "Z"
    case rational = "Q"
    case real = "R"
    case all = ""
}
public enum AMInputType: Equatable {
    case list(AMSelectionStyle)
    case number(AMNumberType)
    case calender(AMCalenderMode)
    case freeText
    case boolian
    case picklist
}

如果你用上面的代码运行你的测试,一切都应该通过。无需为此实现您自己的代码。


更新

在进一步澄清之后,很明显像any/这样的特殊情况all需要在自定义实现中处理。综合实现不会处理这部分。

这是更新的实现 -

  1. 制作所有枚举Equatable,处理它们的特殊情况any/all在这些类型特定的实现中。
  2. 从包含其他枚举作为关联类型的最终枚举中,根据需要利用这些特定于类型的实现。
import Foundation

public enum AMSelectionStyle: Int, Equatable {
    case single
    case multiple
    case any
    
    public static func ==(lhs: Self, rhs: Self) -> Bool {
        let any = Self.any.rawValue
        if lhs.rawValue == any || rhs.rawValue == any {
            return true
        } else {
            return lhs.rawValue == rhs.rawValue
        }
    }
}
public enum AMCalendarMode: Int, Equatable {
    case date
    case time
    case dateTime
    case any
    
    public static func ==(lhs: Self, rhs: Self) -> Bool {
        let any = Self.any.rawValue
        if lhs.rawValue == any || rhs.rawValue == any {
            return true
        } else {
            return lhs.rawValue == rhs.rawValue
        }
    }
}
public enum AMNumberType: String, Equatable {
    case natural = "N"
    case whole = "W"
    case integer = "Z"
    case rational = "Q"
    case real = "R"
    case all = ""
    
    public static func ==(lhs: Self, rhs: Self) -> Bool {
        let all = Self.all.rawValue
        if lhs.rawValue == all || rhs.rawValue == all {
            return true
        } else {
            return lhs.rawValue == rhs.rawValue
        }
    }
}
public enum AMInputType: Equatable {
    case list(AMSelectionStyle)
    case number(AMNumberType)
    case calendar(AMCalendarMode)
    case freeText
    case boolean
    case picklist
    
    public static func ==(lhs: Self, rhs: Self) -> Bool {
        switch (lhs, rhs) {
        case (.list(let l1), .list(let l2)): return l1 == l2
        case (.number(let n1), .number(let n2)): return n1 == n2
        case (.calendar(let c1), .calendar(let c2)): return c1 == c2
        case (.freeText, .freeText), 
             (.boolean, .boolean),
             (.picklist, .picklist): return true
        default: return false
        }
    }
}

测试

var lhs: AMInputType = .number(.all)
var rhs: AMInputType = .number(.real)
print(lhs == rhs) // prints true

推荐阅读