【问题标题】:How do I know the type of an enum with associated value in a generic way?我如何以通用方式知道具有关联值的枚举类型?
【发布时间】:2021-05-09 16:54:42
【问题描述】:

前几天,我问过this question

绝妙的解决方案是这样的:

enum MyEnum {
    case one
    case two
    case three(user: String)
 
    func isOfSameType(_ other: MyEnum) -> Bool {
        switch (self, other) {
        case (.one, .one):
            return true
        case (.two, .two):
            return true
        case (.three, .three):
            return true
        default:
            return false
        }
    }
}
 
var array: [MyEnum] = []
func add(_ value: MyEnum) {
    if array.contains(where: { value.isOfSameType($0) }) { return }
    array.append(value)
}

现在,冷血分析,这似乎完全是黑魔法。

switch怎么能不带参数比较东西呢?

但现在我需要检查stack 上的最后一个元素是否属于three

类似

if array!.last.isOfType(.three) {
}

这行不通。 Xcode 将需要来自 .three 的参数。

因为到目前为止我还不了解我所拥有的,所以我不知道该怎么做。

【问题讨论】:

标签: swift enums swift5


【解决方案1】:

您只需要使您的枚举符合 Equatable 并比较相关的值是否相等:

enum MyEnum: Equatable {
    case one
    case two
    case three(user: String)
    static func == (lhs: Self, rhs: Self) -> Bool {
        switch (lhs, rhs) {
        case (.one, .one): return true
        case (.two, .two): return true
        case let (.three(lhs), .three(rhs)): return lhs == rhs
        default: return false
        }
        
    }
}

var array: [MyEnum] = []
func add(_ value: MyEnum) {
    if array.contains(value) { return }
    array.append(value)
}

add(.one)
add(.one)
array
add(.three(user: "a"))
add(.three(user: "b"))
add(.three(user: "a"))

array[0]  // one
array[1]  // three(user: "a")
array[2]  // three(user: "b")

检查最后一个元素是否为 case .three:

if case .three = array.last {
    print(true)
}

【讨论】:

  • 简直太棒了。 if case?我永远不会猜到。谢谢。
【解决方案2】:

Swift Enum 非常特别,并且有很多其他人没有的功能。然而,它是一个枚举,就像 C 风格的枚举一样简单的整数。枚举的实例只是一个值。

在你的例子中,

enum MyEnum {
    case one
    case two
    case three(user: String)
}

假设你有一个数组,

let arrayMyEnum: [MyEnum] = [.one, .two, .three(user: "Mike"), .two, .three(user: "John")]

那么,你可以,

// basic approach
arrayMyEnum.contains {
    switch $0 {
    case .three(let user):
        return true
    default:
        return false
    }
}

// Using optional pattern
arrayMyEnum.contains {
    if case .three(let user) = $0 {
        return true
    } else {
        return false
    }
}

或者正如您在评论中描述的那样,您不关心包装的值,而只是想检查实例是否是某种情况。然后你可以忽略包装的值,

arrayMyEnum.contains {
    switch $0 {
    case .three:
        return true
    default:
        return false
    }
}

arrayMyEnum.contains {
    if case .three = $0 {
        return true
    } else {
        return false
    }
}

我猜上面是你要求的。

注意,

具有包装值的枚举实例具有用于比较的哈希。因此,如果您想使用 == 相等运算符执行相等,编译器会警告您您的枚举需要符合 Hashable。

例如,如果你想比较,

enum MyEnum: Hashable {
    case one
    case two
    case three(user: String)
}

MyEnum.three(user: "SomeValue") == MyEnum.three(user: "AnotherValue")

本身是 Hashable 的 Swfit 类型的枚举,是隐式可散列的。

在您的情况下,带有包装值的枚举,包装值需要是可散列的。由于 String 已经是 Hashable,你只需要声明 Hashable 的一致性。

仅当包装类型不是 Hashable 时,您需要自己编写 ==hasher 方法,这在大多数情况下非常简单。

读数

Language Guide #Enumerations

Language Reference #Enumeration Case Pattern, Optional Pattern.

【讨论】:

  • 很棒的答案。谢谢。
【解决方案3】:

据我了解,您想知道为什么您的 switch 语句带有

        case (.three, .three):
            return true

可以找到,但是

if array!.last.isOfType(.three) { /* ... */ }

导致编译器错误。

问题是开关语法有点特殊(模式匹配),不能在函数调用中使用。

由于您似乎想忽略 user 的值,您可以执行以下操作:

if case .three = array!.last { /* ... */ }

这正是 switch 语句所做的——它忽略了分配的值。

如果你想调用一个函数,你需要传入一个concrete你的枚举实例,并且由于isOfType忽略了用户,在下面使用一个dummy值声明将起作用:

if array!.last.isOfSameType(.three(user:"dummy")) { /* ... */ }

【讨论】:

    【解决方案4】:

    请试试这个:

    实现三个协议EquatableHashableCustomStringConvertible

    public enum MyEnum: Equatable, Hashable, CustomStringConvertible {
        case one
        case two
        case three(user: String)
        
    //    CustomStringConvertible
    // create unique string for each case
        public var description: String {
            switch self {
                case .one:
                return "one"
                
                case .two:
                return "two"
                
            case .three:
                return "three"
                
            }
        }
        
    //    Hashable
        public func hash(into hasher: inout Hasher) {
            hasher.combine(self.description)
        }
        
    //    Equatable
        public static func == (lhs: MyEnum, rhs: MyEnum) -> Bool {
            return lhs.hashValue == rhs.hashValue
        }
    }
    
    var array: [MyEnum] = []
    
    
       func add(_ value: MyEnum) {
        
        // option one
    //    if array.last!.hashValue == value.hashValue {
    //        return
    //    }
        
        // option two
        if array.contains(value) {
            return
        }
        
        array.append(value)
    }
    
    add(.one)
    add(.one)
    add(.two)
    add(.two)
    add(.three(user: "hello"))
    add(.three(user: "hi"))
    for item in array {
        print(item.description)
    }
    

    结果打印:

    one
    two
    three
    

    【讨论】:

      猜你喜欢
      • 2018-01-31
      • 2022-01-23
      • 1970-01-01
      • 1970-01-01
      • 2014-02-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多