如何枚举String类型的枚举?

 EGO-Underwear 发布于 2023-01-12 05:49

这篇文章与此相关https://www.swift-studies.com/blog/2014/6/10/enumerating-enums-in-swift

基本上提出的解决方案是

enum ProductCategory : String {
     case Washers = "washers", Dryers = "dryers", Toasters = "toasters"

     static let allValues = [Washers, Dryers, Toasters]
}

for category in ProductCategory.allValues{
     //Do something
}

很好,但是......你必须输入两次枚举的元素 - 一次用于枚举,一次用于allValues.并不像人们希望的那样优雅. (181认同)

在Java中,编译器会为您执行此操作,也许Swift 2.0也会这样做.特别是在Java中,所有枚举都获得了一个描述(Java中的toString)方法,该方法将String作为案例名称(Washers,...),并自动创建一组案例.Java还为您提供位置索引.正如我所说,也许是Swift 2.0. (3认同)

同意"但是"......但是正如文章中所述,或许有一个问题,即枚举实际上是一个集合,因此无序......请注意......定义的订单案例不会是一个糟糕的开始! (2认同)


rintaro.. 275

我创建了一个实用函数iterateEnum()来迭代任意enum类型的case .

以下是示例用法:

enum Suit: String {
    case Spades = "?"
    case Hearts = "?"
    case Diamonds = "?"
    case Clubs = "?"
}

for f in iterateEnum(Suit) {
    println(f.rawValue)
}

输出:

?
?
?
?

但是,这仅用于调试或测试目的:这依赖于几个未记录的当前(Swift1.1)编译器行为.所以,使用它需要你自担风险:)

这是代码:

func iterateEnum(_: T.Type) -> GeneratorOf {
    var cast: (Int -> T)!
    switch sizeof(T) {
        case 0: return GeneratorOf(GeneratorOfOne(unsafeBitCast((), T.self)))
        case 1: cast = { unsafeBitCast(UInt8(truncatingBitPattern: $0), T.self) }
        case 2: cast = { unsafeBitCast(UInt16(truncatingBitPattern: $0), T.self) }
        case 4: cast = { unsafeBitCast(UInt32(truncatingBitPattern: $0), T.self) }
        case 8: cast = { unsafeBitCast(UInt64($0), T.self) }
        default: fatalError("cannot be here")
    }

    var i = 0
    return GeneratorOf {
        let next = cast(i)
        return next.hashValue == i++ ? next : nil
    }
}

根本的想法是:

内存表示enum- 不包括enum具有关联类型的s - 只是案例的索引,当案例的计数是2...256,它与UInt8,何时257...65536,UInt16等等时相同.因此,它可以unsafeBitcast来自相应的无符号整数类型.

.hashValue 枚举值的大小与大小写的索引相同.

.hashValue无效索引中获取的枚举值是0


添加:

修订了Swift2,并从@ Kametrixom的答案中实现了铸造思路

func iterateEnum(_: T.Type) -> AnyGenerator {
    var i = 0
    return anyGenerator {
        let next = withUnsafePointer(&i) { UnsafePointer($0).memory }
        return next.hashValue == i++ ? next : nil
    }
}

增加: 修改为Swift3

func iterateEnum(_: T.Type) -> AnyIterator {
    var i = 0
    return AnyIterator {
        let next = withUnsafePointer(to: &i) {
            $0.withMemoryRebound(to: T.self, capacity: 1) { $0.pointee }
        }
        if next.hashValue != i { return nil }
        i += 1
        return next
    }
}

增加: 针对Swift3.0.1进行了修订

func iterateEnum(_: T.Type) -> AnyIterator {
    var i = 0
    return AnyIterator {
        let next = withUnsafeBytes(of: &i) { $0.load(as: T.self) }
        if next.hashValue != i { return nil }
        i += 1
        return next
    }
}

真棒,唯一回答这个问题的答案!但是......不会碰它!但努力+1! (20认同)

+1这非常棒.它也是,恕我直言,使用起来太聪明了,正如它在每个主要的Swift版本变化中显着突破所证明的那样.据作者所说,Swift 3版本是在Swift 3退出测试版之前的一个月完成的...如果你要接受这个答案并且学习所有这些`withUnsafePointer``withMemoryRebound`和`pointee`的东西,那么使用这一切都是这样的.否则,我会避免它. (16认同)

我只是想添加这个现在已经在swift 4中打破了,但只在linux上,所以+1给上面的评论说这太聪明了. (5认同)

Swift 3版本效果很好.只需要稍微修改一下用法:对于f in iterateEnum(Suit.self){print(f.rawValue)} (2认同)


Cœur.. 230

Swift 4.2+

从Swift 4.2开始(使用Xcode 10),只需添加协议一致性即可CaseIterableallCases以下方面受益:

extension Suit: CaseIterable {}

然后,这将打印所有可能的值:

Suit.allCases.forEach {
    print($0.rawValue)
}

与早期Swift版本的兼容性(3.x和4.x)

只是模仿Swift 4.2的实现:

#if !swift(>=4.2)
public protocol CaseIterable {
    associatedtype AllCases: Collection where AllCases.Element == Self
    static var allCases: AllCases { get }
}
extension CaseIterable where Self: Hashable {
    static var allCases: [Self] {
        return [Self](AnySequence { () -> AnyIterator in
            var raw = 0
            var first: Self?
            return AnyIterator {
                let current = withUnsafeBytes(of: &raw) { $0.load(as: Self.self) }
                if raw == 0 {
                    first = current
                } else if current == first {
                    return nil
                }
                raw += 1
                return current
            }
        })
    }
}
#endif


sdduursma.. 129

其他解决方案有效但它们都假设例如可能的等级和套装的数量,或者第一和最后等级可能是什么.诚然,在可预见的未来,一副牌的布局可能不会有太大变化.但是,一般来说,编写尽可能少的假设的代码更为简洁.我的解决方案

我已经在套装枚举中添加了原始类型,因此我可以使用Suit(rawValue :)来访问Suit案例:

enum Suit: Int {
    case Spades = 1
    case Hearts, Diamonds, Clubs
    func simpleDescription() -> String {
        switch self {
            case .Spades:
                return "spades"
            case .Hearts:
                return "hearts"
            case .Diamonds:
                return "diamonds"
            case .Clubs:
                return "clubs"
        }
    }
    func color() -> String {
        switch self {
        case .Spades:
            return "black"
        case .Clubs:
            return "black"
        case .Diamonds:
            return "red"
        case .Hearts:
            return "red"
        }
    }
}

enum Rank: Int {
    case Ace = 1
    case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
    case Jack, Queen, King
    func simpleDescription() -> String {
        switch self {
            case .Ace:
                return "ace"
            case .Jack:
                return "jack"
            case .Queen:
                return "queen"
            case .King:
                return "king"
            default:
                return String(self.rawValue)
        }
    }
}

下面是Card的createDeck()方法的实现.init(rawValue :)是一个可用的初始化程序并返回一个可选项.通过在while语句中解包和检查它的值,不需要假设Rank或Suit案例的数量:

struct Card {
    var rank: Rank
    var suit: Suit
    func simpleDescription() -> String {
        return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
    }
    func createDeck() -> [Card] {
        var n = 1
        var deck = [Card]()
        while let rank = Rank(rawValue: n) {
            var m = 1
            while let suit = Suit(rawValue: m) {
                deck.append(Card(rank: rank, suit: suit))
                m += 1
            }
            n += 1
        }
        return deck
    }
}

以下是调用createDeck方法的方法:

let card = Card(rank: Rank.Ace, suit: Suit.Clubs)
let deck = card.createDeck()

绝对最佳答案我在这个主题的各种主题上看到过.十分优雅.这适用于Int类型枚举,但我想知道如何迭代其他类型(字符串,自定义类型等). (12认同)

这假设原始值是连续的.如果不是这样,例如当枚举表示位掩码标志时,循环过早退出. (10认同)

这绝对是最好的解决方案.有一点需要注意.在本书的例子中,sdduursma没有"case Spades = 1".起初我没有抓住这个.这是一个选项,或者你可以只使用"var m = 0" (3认同)


Kametrixom.. 75

第二个答案确实有效

所以我在位和字节中偶然发现并创建了一个扩展(我后来发现它与@rintaro的答案非常相似).它可以像这样使用:

enum E : EnumCollection {
    case A, B, C
}

Array(E.cases())    // [A, B, C]

值得注意的是它可以在任何枚举上使用(没有相关值).请注意,这不适用于没有案例的枚举.

放弃

与@rintaro的回答一样,此代码使用枚举的基础表示.这种表示没有记录,将来可能会改变,这会破坏它 - >我不建议在生产中使用它.

代码(Swift 2.2,Xcode 7.3.1,不适用于Xcode 10)

protocol EnumCollection : Hashable {}
extension EnumCollection {
    static func cases() -> AnySequence {
        typealias S = Self
        return AnySequence { () -> AnyGenerator in
            var raw = 0
            return AnyGenerator {
                let current : Self = withUnsafePointer(&raw) { UnsafePointer($0).memory }
                guard current.hashValue == raw else { return nil }
                raw += 1
                return current
            }
        }
    }
}

代码(Swift 3,Xcode 8.1,不适用于Xcode 10)

protocol EnumCollection : Hashable {}
extension EnumCollection {
    static func cases() -> AnySequence {
        typealias S = Self
        return AnySequence { () -> AnyIterator in
            var raw = 0
            return AnyIterator {
                let current : Self = withUnsafePointer(to: &raw) { $0.withMemoryRebound(to: S.self, capacity: 1) { $0.pointee } }
                guard current.hashValue == raw else { return nil }
                raw += 1
                return current
            }
        }
    }
}

(我不知道为什么我需要它typealias,但编译器抱怨没有它)

(我对这个答案进行了大的修改,看过去版本的编辑)

14 个回答
  • 我创建了一个实用函数iterateEnum()来迭代任意enum类型的case .

    以下是示例用法:

    enum Suit: String {
        case Spades = "?"
        case Hearts = "?"
        case Diamonds = "?"
        case Clubs = "?"
    }
    
    for f in iterateEnum(Suit) {
        println(f.rawValue)
    }
    

    输出:

    ?
    ?
    ?
    ?
    

    但是,这仅用于调试或测试目的:这依赖于几个未记录的当前(Swift1.1)编译器行为.所以,使用它需要你自担风险:)

    这是代码:

    func iterateEnum<T: Hashable>(_: T.Type) -> GeneratorOf<T> {
        var cast: (Int -> T)!
        switch sizeof(T) {
            case 0: return GeneratorOf(GeneratorOfOne(unsafeBitCast((), T.self)))
            case 1: cast = { unsafeBitCast(UInt8(truncatingBitPattern: $0), T.self) }
            case 2: cast = { unsafeBitCast(UInt16(truncatingBitPattern: $0), T.self) }
            case 4: cast = { unsafeBitCast(UInt32(truncatingBitPattern: $0), T.self) }
            case 8: cast = { unsafeBitCast(UInt64($0), T.self) }
            default: fatalError("cannot be here")
        }
    
        var i = 0
        return GeneratorOf {
            let next = cast(i)
            return next.hashValue == i++ ? next : nil
        }
    }
    

    根本的想法是:

    内存表示enum- 不包括enum具有关联类型的s - 只是案例的索引,当案例的计数是2...256,它与UInt8,何时257...65536,UInt16等等时相同.因此,它可以unsafeBitcast来自相应的无符号整数类型.

    .hashValue 枚举值的大小与大小写的索引相同.

    .hashValue无效索引中获取的枚举值是0


    添加:

    修订了Swift2,并从@ Kametrixom的答案中实现了铸造思路

    func iterateEnum<T: Hashable>(_: T.Type) -> AnyGenerator<T> {
        var i = 0
        return anyGenerator {
            let next = withUnsafePointer(&i) { UnsafePointer<T>($0).memory }
            return next.hashValue == i++ ? next : nil
        }
    }
    

    增加: 修改为Swift3

    func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> {
        var i = 0
        return AnyIterator {
            let next = withUnsafePointer(to: &i) {
                $0.withMemoryRebound(to: T.self, capacity: 1) { $0.pointee }
            }
            if next.hashValue != i { return nil }
            i += 1
            return next
        }
    }
    

    增加: 针对Swift3.0.1进行了修订

    func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> {
        var i = 0
        return AnyIterator {
            let next = withUnsafeBytes(of: &i) { $0.load(as: T.self) }
            if next.hashValue != i { return nil }
            i += 1
            return next
        }
    }
    

    2023-01-12 05:52 回答
  • 其他解决方案有效但它们都假设例如可能的等级和套装的数量,或者第一和最后等级可能是什么.诚然,在可预见的未来,一副牌的布局可能不会有太大变化.但是,一般来说,编写尽可能少的假设的代码更为简洁.我的解决方案

    我已经在套装枚举中添加了原始类型,因此我可以使用Suit(rawValue :)来访问Suit案例:

    enum Suit: Int {
        case Spades = 1
        case Hearts, Diamonds, Clubs
        func simpleDescription() -> String {
            switch self {
                case .Spades:
                    return "spades"
                case .Hearts:
                    return "hearts"
                case .Diamonds:
                    return "diamonds"
                case .Clubs:
                    return "clubs"
            }
        }
        func color() -> String {
            switch self {
            case .Spades:
                return "black"
            case .Clubs:
                return "black"
            case .Diamonds:
                return "red"
            case .Hearts:
                return "red"
            }
        }
    }
    
    enum Rank: Int {
        case Ace = 1
        case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
        case Jack, Queen, King
        func simpleDescription() -> String {
            switch self {
                case .Ace:
                    return "ace"
                case .Jack:
                    return "jack"
                case .Queen:
                    return "queen"
                case .King:
                    return "king"
                default:
                    return String(self.rawValue)
            }
        }
    }
    

    下面是Card的createDeck()方法的实现.init(rawValue :)是一个可用的初始化程序并返回一个可选项.通过在while语句中解包和检查它的值,不需要假设Rank或Suit案例的数量:

    struct Card {
        var rank: Rank
        var suit: Suit
        func simpleDescription() -> String {
            return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
        }
        func createDeck() -> [Card] {
            var n = 1
            var deck = [Card]()
            while let rank = Rank(rawValue: n) {
                var m = 1
                while let suit = Suit(rawValue: m) {
                    deck.append(Card(rank: rank, suit: suit))
                    m += 1
                }
                n += 1
            }
            return deck
        }
    }
    

    以下是调用createDeck方法的方法:

    let card = Card(rank: Rank.Ace, suit: Suit.Clubs)
    let deck = card.createDeck()
    

    2023-01-12 05:52 回答
  • 这篇文章与此相关https://www.swift-studies.com/blog/2014/6/10/enumerating-enums-in-swift

    基本上提出的解决方案是

    enum ProductCategory : String {
         case Washers = "washers", Dryers = "dryers", Toasters = "toasters"
    
         static let allValues = [Washers, Dryers, Toasters]
    }
    
    for category in ProductCategory.allValues{
         //Do something
    }
    

    2023-01-12 05:56 回答
  • 第二个答案确实有效

    所以我在位和字节中偶然发现并创建了一个扩展(我后来发现它与@rintaro的答案非常相似).它可以像这样使用:

    enum E : EnumCollection {
        case A, B, C
    }
    
    Array(E.cases())    // [A, B, C]
    

    值得注意的是它可以在任何枚举上使用(没有相关值).请注意,这不适用于没有案例的枚举.

    放弃

    与@rintaro的回答一样,此代码使用枚举的基础表示.这种表示没有记录,将来可能会改变,这会破坏它 - >我不建议在生产中使用它.

    代码(Swift 2.2,Xcode 7.3.1,不适用于Xcode 10)

    protocol EnumCollection : Hashable {}
    extension EnumCollection {
        static func cases() -> AnySequence<Self> {
            typealias S = Self
            return AnySequence { () -> AnyGenerator<S> in
                var raw = 0
                return AnyGenerator {
                    let current : Self = withUnsafePointer(&raw) { UnsafePointer($0).memory }
                    guard current.hashValue == raw else { return nil }
                    raw += 1
                    return current
                }
            }
        }
    }
    

    代码(Swift 3,Xcode 8.1,不适用于Xcode 10)

    protocol EnumCollection : Hashable {}
    extension EnumCollection {
        static func cases() -> AnySequence<Self> {
            typealias S = Self
            return AnySequence { () -> AnyIterator<S> in
                var raw = 0
                return AnyIterator {
                    let current : Self = withUnsafePointer(to: &raw) { $0.withMemoryRebound(to: S.self, capacity: 1) { $0.pointee } }
                    guard current.hashValue == raw else { return nil }
                    raw += 1
                    return current
                }
            }
        }
    }
    

    (我不知道为什么我需要它typealias,但编译器抱怨没有它)

    (我对这个答案进行了大的修改,看过去版本的编辑)

    2023-01-12 06:01 回答
  • 您可以通过实现ForwardIndexType协议来遍历枚举.

    ForwardIndexType协议要求您定义一个successor()函数来逐步执行元素.

    enum Rank: Int, ForwardIndexType {
        case Ace = 1
        case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
        case Jack, Queen, King
    
        // ... other functions
    
        // Option 1 - Figure it out by hand
        func successor() -> Rank {
            switch self {
                case .Ace:
                  return .Two
                case .Two:
                  return .Three
    
                // ... etc.
    
                default:
                  return .King
            }
        }
    
        // Option 2 - Define an operator!
        func successor() -> Rank {
            return self + 1
        }
    }
    
    // NOTE: The operator is defined OUTSIDE the class
    func + (left: Rank, right: Int) -> Rank {
        // I'm using to/from raw here, but again, you can use a case statement
        // or whatever else you can think of
    
        return left == .King ? .King : Rank(rawValue: left.rawValue + right)!
    }
    

    在开放或闭合范围(..<...)上迭代将在内部调用successor()允许您编写此函数的函数:

    // Under the covers, successor(Rank.King) and successor(Rank.Ace) are called to establish limits
    for r in Rank.Ace...Rank.King {
        // Do something useful
    }
    

    2023-01-12 06:03 回答
  • 如果给enum 一个原始的Int值,它将使循环更容易.

    例如,您可以使用anyGenerator获取可以枚举您的值的生成器:

    enum Suit: Int, CustomStringConvertible {
        case Spades, Hearts, Diamonds, Clubs
        var description: String {
            switch self {
            case .Spades:   return "Spades"
            case .Hearts:   return "Hearts"
            case .Diamonds: return "Diamonds"
            case .Clubs:    return "Clubs"
            }
        }
        static func enumerate() -> AnyGenerator<Suit> {
            var nextIndex = Spades.rawValue
            return anyGenerator { Suit(rawValue: nextIndex++) }
        }
    }
    // You can now use it like this:
    for suit in Suit.enumerate() {
        suit.description
    }
    // or like this:
    let allSuits: [Suit] = Array(Suit.enumerate())
    

    然而,这看起来像一个相当常见的模式,如果我们可以通过简单地符合协议使任何枚举类型可枚举,那不是很好吗?好了Swift 2.0和协议扩展,现在我们可以!

    只需将其添加到您的项目中:

    protocol EnumerableEnum {
        init?(rawValue: Int)
        static func firstValue() -> Int
    }
    extension EnumerableEnum {
        static func enumerate() -> AnyGenerator<Self> {
            var nextIndex = firstRawValue()
            return anyGenerator { Self(rawValue: nextIndex++) }
        }
        static func firstRawValue() -> Int { return 0 }
    }
    

    现在,只要您创建枚举(只要它具有Int原始值),您就可以通过符合协议使其可枚举:

    enum Rank: Int, EnumerableEnum {
        case Ace, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King
    }
    // ...
    for rank in Rank.enumerate() { ... }
    

    如果您的枚举值不以0(默认值)开头,则覆盖该firstRawValue方法:

    enum DeckColor: Int, EnumerableEnum {
        case Red = 10, Blue, Black
        static func firstRawValue() -> Int { return Red.rawValue }
    }
    // ...
    let colors = Array(DeckColor.enumerate())
    

    最后西服类,包括更换simpleDescription与更标准CustomStringConvertible协议,将是这样的:

    enum Suit: Int, CustomStringConvertible, EnumerableEnum {
        case Spades, Hearts, Diamonds, Clubs
        var description: String {
            switch self {
            case .Spades:   return "Spades"
            case .Hearts:   return "Hearts"
            case .Diamonds: return "Diamonds"
            case .Clubs:    return "Clubs"
            }
        }
    }
    // ...
    for suit in Suit.enumerate() {
        print(suit.description)
    }
    

    编辑:

    Swift 3 句法:

    protocol EnumerableEnum {
        init?(rawValue: Int)
        static func firstRawValue() -> Int
    }
    
    extension EnumerableEnum {
        static func enumerate() -> AnyIterator<Self> {
            var nextIndex = firstRawValue()
    
            let iterator: AnyIterator<Self> = AnyIterator {
                defer { nextIndex = nextIndex + 1 }
                return Self(rawValue: nextIndex)
            }
    
            return iterator
        }
    
        static func firstRawValue() -> Int {
            return 0
        }
    }
    

    2023-01-12 06:07 回答
  • 原则上可以这样做,假设你没有为枚举的情况使用原始值赋值:

    enum RankEnum: Int {
      case Ace
      case One
      case Two
    }
    
    class RankEnumGenerator: Generator {
        var i = 0
        typealias Element = RankEnum
        func next() -> Element? {
            let r = RankEnum.fromRaw(i)
            i += 1
            return r
        }
    }
    
    extension RankEnum {
        static func enumerate() -> SequenceOf<RankEnum> {
            return SequenceOf<RankEnum>({ RankEnumGenerator() })
        }
    }
    
    for r in RankEnum.enumerate() {
        println("\(r.toRaw())")
    }
    

    2023-01-12 06:09 回答
  • 这个问题现在变得容易多了.这是我的Swift 4.2解决方案.

    enum Suit: Int, CaseIterable {
        case None
        case Spade, Heart, Diamond, Club
    
        static let allNonNullCases = Suit.allCases[Spade.rawValue...]
    }
    
    enum Rank: Int, CaseIterable {
        case Joker
        case Two, Three, Four, Five, Six, Seven, Eight
        case Nine, Ten, Jack, Queen, King, Ace
    
        static let allNonNullCases = Rank.allCases[Two.rawValue...]
    }
    
    func makeDeck(withJoker: Bool = false) -> [Card] {
        var deck = [Card]()
        for suit in Suit.allNonNullCases {
            for rank in Rank.allNonNullCases {
                deck.append(Card(suit: suit, rank: rank))
            }
        }
        if withJoker {
            deck.append(Card(suit: .None, rank: .Joker))
        }
        return deck
    }
    

    前4.2

    我喜欢这个在找到这个页面后放在一起的解决方案: Swift中的列表理解

    它使用Int raws而不是Strings但它避免键入两次,它允许自定义范围,并且不硬编码原始值.

    这是我原始解决方案的Swift 4版本,但请参见上面的4.2改进.

    enum Suit: Int {
        case None
        case Spade, Heart, Diamond, Club
    
        static let allRawValues = Suit.Spade.rawValue...Suit.Club.rawValue
        static let allCases = Array(allRawValues.map{ Suit(rawValue: $0)! })
    }
    enum Rank: Int {
        case Joker
        case Two, Three, Four, Five, Six
        case Seven, Eight, Nine, Ten
        case Jack, Queen, King, Ace
    
        static let allRawValues = Rank.Two.rawValue...Rank.Ace.rawValue
        static let allCases = Array(allRawValues.map{ Rank(rawValue: $0)! })
    }
    func makeDeck(withJoker: Bool = false) -> [Card] {
        var deck = [Card]()
        for suit in Suit.allCases {
            for rank in Rank.allCases {
                deck.append(Card(suit: suit, rank: rank))
            }
        }
        if withJoker {
            deck.append(Card(suit: .None, rank: .Joker))
        }
        return deck
    }
    

    2023-01-12 06:11 回答
  • 我发现自己在.allValues整个代码中都做了很多.我终于想出了一种简单地符合Iteratable协议并有rawValues()方法的方法.

    protocol Iteratable {}
    extension RawRepresentable where Self: RawRepresentable {
    
        static func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> {
            var i = 0
            return AnyIterator {
                let next = withUnsafePointer(to: &i) {
                    $0.withMemoryRebound(to: T.self, capacity: 1) { $0.pointee }
                }
                if next.hashValue != i { return nil }
                i += 1
                return next
            }
        }
    }
    
    extension Iteratable where Self: RawRepresentable, Self: Hashable {
        static func hashValues() -> AnyIterator<Self> {
            return iterateEnum(self)
        }
    
        static func rawValues() -> [Self.RawValue] {
            return hashValues().map({$0.rawValue})
        }
    }
    
    
    // Example
    enum Grocery: String, Iteratable {
        case Kroger = "kroger"
        case HEB = "h.e.b."
        case Randalls = "randalls"
    }
    
    let groceryHashes = Grocery.hashValues() // AnyIterator<Grocery>
    let groceryRawValues = Grocery.rawValues() // ["kroger", "h.e.b.", "randalls"]
    

    2023-01-12 06:12 回答
  • Swift 4.2+

    从Swift 4.2开始(使用Xcode 10),只需添加协议一致性即可CaseIterableallCases以下方面受益:

    extension Suit: CaseIterable {}
    

    然后,这将打印所有可能的值:

    Suit.allCases.forEach {
        print($0.rawValue)
    }
    

    与早期Swift版本的兼容性(3.x和4.x)

    只是模仿Swift 4.2的实现:

    #if !swift(>=4.2)
    public protocol CaseIterable {
        associatedtype AllCases: Collection where AllCases.Element == Self
        static var allCases: AllCases { get }
    }
    extension CaseIterable where Self: Hashable {
        static var allCases: [Self] {
            return [Self](AnySequence { () -> AnyIterator<Self> in
                var raw = 0
                var first: Self?
                return AnyIterator {
                    let current = withUnsafeBytes(of: &raw) { $0.load(as: Self.self) }
                    if raw == 0 {
                        first = current
                    } else if current == first {
                        return nil
                    }
                    raw += 1
                    return current
                }
            })
        }
    }
    #endif
    

    2023-01-12 06:13 回答
  • 带有Swift 4.2的Xcode 10

    enum Filter: String, CaseIterable {
    
        case salary = "Salary"
        case experience = "Experience"
        case technology = "Technology"
        case unutilized = "Unutilized"
        case unutilizedHV = "Unutilized High Value"
    
        static let allValues = Filter.allCases.map { $0.rawValue }
    }
    

    称它为

    print(Filter.allValues)
    

    印刷品:

    [“工资”,“经验”,“技术”,“未利用”,“未利用的高价值”]


    旧版本

    为了enum代表Int

    enum Filter: Int {
        case salary
        case experience
        case technology
        case unutilized
        case unutilizedHV
    
        static let allRawValues = salary.rawValue...unutilizedHV.rawValue  // First to last case
        static let allValues = allRawValues.map { Filter(rawValue: $0)!.rawValue }
    }
    

    这样称呼它:

    print(Filter.allValues)
    

    印刷品:

    [0,1,2,3,4]


    为了enum代表String

    enum Filter: Int {
        case salary
        case experience
        case technology
        case unutilized
        case unutilizedHV
    
        static let allRawValues = salary.rawValue...unutilizedHV.rawValue  // First to last case
        static let allValues = allRawValues.map { Filter(rawValue: $0)!.description }
    }
    
    extension Filter: CustomStringConvertible {
        var description: String {
            switch self {
            case .salary: return "Salary"
            case .experience: return "Experience"
            case .technology: return "Technology"
            case .unutilized: return "Unutilized"
            case .unutilizedHV: return "Unutilized High Value"
            }
        }
    }
    

    称它为

    print(Filter.allValues)
    

    印刷品:

    [“工资”,“经验”,“技术”,“未利用”,“未利用的高价值”]

    2023-01-12 06:13 回答
  • 更新为Swift 2.2 +

    func iterateEnum<T: Hashable>(_: T.Type) -> AnyGenerator<T> {
        var i = 0
        return AnyGenerator {
            let next = withUnsafePointer(&i) {
                UnsafePointer<T>($0).memory
            }
            if next.hashValue == i {
                i += 1
                return next
            } else {
                return nil
            }
        }
    }
    

    它的更新代码雨燕2.2的形式@ Kametrixom是一个 swer

    适用于Swift 3.0+(非常感谢@Philip)

    func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> {
        var i = 0
        return AnyIterator {
            let next = withUnsafePointer(&i) {
                UnsafePointer<T>($0).pointee
            }
            if next.hashValue == i {
                i += 1
                return next
            } else {
                return nil
            }
        }
    }
    

    2023-01-12 06:14 回答
  • 编辑: Swift进化提案 SE-0194衍生的Enum案例集提出了这个问题的一个解决方案.我们在Swift 4.2和更新版本中看到它.该提案还指出了一些与此处已经提到的类似的解决方法,但仍然可能会有趣.

    为了完整起见,我还会保留原帖.


    这是基于@ Peymmankh的回答的另一种方法,适用于Swift 3.

    public protocol EnumCollection: Hashable {}
    
    extension EnumCollection {
    
    public static func allValues() -> [Self] {
        typealias S = Self
    
        let retVal = AnySequence { () -> AnyIterator<S> in
            var raw = 0
            return AnyIterator {
                let current = withUnsafePointer(to: &raw) {
                     $0.withMemoryRebound(to: S.self, capacity: 1) { $0.pointee }
                }
                guard current.hashValue == raw else { return nil }
                raw += 1
                return current
            }
        }
    
        return [S](retVal)
    }
    

    2023-01-12 06:29 回答
  • enum Rank: Int {
        ...
        static let ranks = (Rank.Ace.rawValue ... Rank.King.rawValue).map{Rank(rawValue: $0)! }
    
    }
    enum Suit {
        ...
        static let suits = [Spades, Hearts, Diamonds, Clubs]
    }
    
    struct Card {
        ...
        static func fullDesk() -> [Card] {
            var desk: [Card] = []
            for suit in Suit.suits {
                for rank in Rank.ranks {
                    desk.append(Card(rank: rank,suit: suit))
                }
            }
            return desk
        }
    }
    

    这个怎么样?

    2023-01-12 06:37 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有