【问题标题】:can we override switch case behaviour like we override operator == with Equatable protocol in Swift我们能否像在 Swift 中使用 Equatable 协议覆盖 operator == 一样覆盖 switch case 行为
【发布时间】:2021-09-04 21:18:08
【问题描述】:

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

【问题讨论】:

    标签: ios swift switch-statement operator-overloading operator-keyword


    【解决方案1】:

    Swift 可以为 enum 合成 Equatable 一致性,只要它的所有案例(及其相关值)都符合 Equatable

    所以默认情况下你会得到一个正确的实现,你只需要在所有必要的地方标记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
    

    【讨论】:

    • 同意,但这仅在调用 Equatable 协议时才有效。但是这里 Equitable 协议在 switch case 中使用枚举时不会被调用。
    • 我已更改为实现所有枚举以实现 Equitable 但不起作用。
    • 您的哪一项测试失败了?你期待它做什么?请分享上述设置产生问题的代码。
    • OP 有一些非常特殊的要求,因此无法使用默认的 Equatable 实现来解决此问题
    • 你的意思是我必须使用 if else 而不是 switch case then。
    【解决方案2】:

    在开关中使用元组时,您可以选择具有通配符大小写的类型,其中一侧可以是任何东西,您可以在这里使用它来有效地检查 .any.all

    所以覆盖 == 以便 .any/.all 对您在 AMInputType 中使用的所有 3 个枚举按预期工作,并且您的比较将起作用。

    public enum AMSelectionStyle: Equatable{
        case single
        case multiple
        case any
    
        public static func ==(lhs: AMSelectionStyle, rhs: AMSelectionStyle) -> Bool {
            switch (lhs, rhs) {
            case (_, .any):
                return true
            case (.any, _):
                return true
            case (.single, .single),
                (.multiple, .multiple):
                return true
            default:
                return false
            }
        }
    }
    
    public enum AMCalenderMode: Equatable{
        case date
        case time
        case dateTime
        case any
    
        public static func ==(lhs: AMCalenderMode, rhs: AMCalenderMode) -> Bool {
            switch (lhs, rhs) {
            case (_, .any):
                return true
            case (.any, _):
                return true
            case (.date, .date),
                (.time, .time),
                (.dateTime, .dateTime):
                return true
            default:
                return false
            }
        }
    }
    
    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: AMNumberType, rhs: AMNumberType) -> Bool {
            switch (lhs, rhs) {
            case (_, .all):
                return true
            case (.all, _):
                return true
            case (.natural, .natural),
                 (.whole, .whole),
                 (.integer, .integer),
                 (.rational, .rational),
                 (.real, .real):
                return true
            default:
                return false
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      我已经向苹果提出了申请,并收到了他们的以下回复。

      This is correct behavior because switch statement doesn’t use `==` operator to match cases, instead it uses `~=`. Try adding `~=` as well for `AMInputType`.
      

      但即使在覆盖运算符 ~= 之后它也不起作用。

      编辑 以下是苹果对问题的新反馈。

      在查看您的反馈后,我们为您提供了一些额外信息,或者一些额外信息,或者对于此问题需要采取行动:

      这是模式匹配的预期行为。当涉及的模式是表达式时,Swift 允许使用 ~= 运算符重载具有用户定义语义的 switch-case 语句。常规模式将始终使用 Swift 内置的模式匹配语义进行匹配。 Swift 参考手册中关于模式匹配的部分详细介绍了在这些情况下语言的行为:https://docs.swift.org/swift-book/ReferenceManual/Patterns.html#grammar_expression-pattern

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-10-04
        • 2018-12-31
        • 1970-01-01
        • 2015-06-26
        • 1970-01-01
        • 2017-05-11
        • 1970-01-01
        • 2015-05-21
        相关资源
        最近更新 更多