【问题标题】:How to enumerate an enum with String type?如何枚举字符串类型的枚举?
【发布时间】:2014-07-23 08:00:59
【问题描述】:
enum Suit: String {
    case spades = "♠"
    case hearts = "♥"
    case diamonds = "♦"
    case clubs = "♣"
}

例如,我该怎么做:

for suit in Suit {
    // do something with suit
    print(suit.rawValue)
}

结果示例:

♠
♥
♦
♣

【问题讨论】:

  • 在什么情况下你会不知道类型?
  • 你是对的,这里是String类型。
  • 在 Swift 中还没有反射...
  • 它们被称为枚举是不是很讽刺,但是在 Swift 中枚举它们却是如此令人痛苦?
  • @CharltonProvatas 如果这是 Swift 中唯一的缺点,我会收工。看看有多少人为此提供了不同的解决方法,我只是在啃我的键盘。

标签: swift string enums enumerate


【解决方案1】:

更新代码:Swift 4.2/Swift 5

enum Suit: String, CaseIterable {
   case spades = "♠"
   case hearts = "♥"
   case diamonds = "♦"
   case clubs = "♣"
}

根据问题访问输出:

for suitKey in Suit.allCases {
    print(suitKey.rawValue)
}

输出:

♠
♥
♦
♣

CaseIterable: 提供其所有值的集合。 符合 CaseIterable 协议的类型通常是没有关联值的枚举。使用 CaseIterable 类型时,您可以使用该类型的 allCases 属性访问该类型所有案例的集合。

为了访问案例,我们使用 .allCases。更多信息请点击https://developer.apple.com/documentation/swift/caseiterable

【讨论】:

    【解决方案2】:

    如果你给枚举 a raw Int value 它将使循环更容易。

    例如,您可以使用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())
    

    最终的 Suit 类,包括将 simpleDescription 替换为 more standard CustomStringConvertible protocol,将如下所示:

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

    【讨论】:

    • nextIndex++ 将在 swift 3 中删除。您建议替换为 - var nextIndex = firstRawValue() return anyGenerator { Self(rawValue: nextIndex++) }
    • 想通了。 defer { nextIndex += 1 } return AnyGenerator { Self(rawValue: nextIndex) }
    【解决方案3】:

    这个问题现在容易多了。这是我的 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 之前的:

    我喜欢我在找到“List comprehension in 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
    }
    

    【讨论】:

    • 哦,我现在知道我的和Sutean Rutjanalard的基本一样了。
    • 实际上,我更喜欢你的实现。我觉得更清楚了! 1 票。实际上,投票最多的答案太聪明了,将来肯定会崩溃。你的承诺在未来会有一定的稳定性。
    • 您可以将所有案例现代化为:static let allCases = allRawValues.compactMap { Rank(rawValue: $0) }
    【解决方案4】:

    我偶然发现了位和字节,并创建了一个扩展,后来我发现它与@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,但编译器会抱怨没有它。

    【讨论】:

    • 这个答案比我的答案还要好,尤其是在选角方面:)
    • 但我认为这只适用于小端环境?
    • Xcode 8 beta 6 再次改变了这一点!我收到以下错误:“init”不可用:使用“withMemoryRebound(to:capacity:_)”临时将内存视为另一种布局兼容类型。`
    • @ConfusedVorlon:请参阅上面@Rintaro 的答案:将withUnsafePointer...pointee} 替换为withUnsafePointer(to: &amp;i) { $0.withMemoryRebound(to: T.self, capacity: 1) { $0.pointee } }
    • 从 Xcode 10 开始,这似乎不再有效。(我知道 Swift 4.2 不需要这样做)但是在 Xcode 10 中使用 Swift 4(.1) 时,此代码不再有效(原始值不等)
    【解决方案5】:

    我创建了一个实用函数 iterateEnum() 用于迭代任意 enum 类型的案例。

    以下是示例用法:

    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的内存表示,不包括关联类型的enums,当事例数为2...256时,它只是一个事例的索引,它与UInt8相同,当257...65536时,它是@ 987654333@等。因此,它可以是对应的无符号整数类型中的unsafeBitcast
    • .hashValue 的枚举值与案例的索引相同。
    • invalid 索引位转换的枚举值的.hashValue0

    针对 Swift2 进行了修订,并实现了来自 @Kametrixom's answer 的铸造想法:

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

    【讨论】:

    • 太棒了,唯一能回答这个问题的答案!但是是的......不会碰它!不过,为努力 +1!
    • 我刚刚发布了my answer,它的工作方式基本相同(后来才看到这个答案)。它使用 Swift 2.0 beta 6 和该语言的现代特性。
    • Swift 3 版本运行良好。只需要稍微修改一下用法:for f in iterateEnum(Suit.self) { print(f.rawValue) }
    • +1 这太棒了。它也是,恕我直言,使用起来太聪明了,正如它在每个主要的 Swift 版本更改中都显着破坏所证明的那样。值得称赞的是,Swift 3 版本是在 Swift 3 推出测试版前一个月完成的......如果你要接受这个答案并学习所有这些 withUnsafePointer withMemoryReboundpointee 的东西,然后使用无论如何。否则,我会避免它。
    • 我只想添加这个现在在 swift 4 中被破坏了,但只在 linux 上,所以 +1 到上面的 cmets,这太聪明了,无法使用。
    【解决方案6】:

    斯威夫特 4.2+

    Swift 4.2(使用Xcode 10)开始,只需将协议一致性添加到CaseIterable 即可从allCases 中受益。要添加此协议一致性,您只需在某处编写:

    extension Suit: CaseIterable {}
    

    如果枚举是你自己的,你可以直接在声明中指定一致性:

    enum Suit: String, CaseIterable { case spades = "♠"; case hearts = "♥"; case diamonds = "♦"; case clubs = "♣" }
    

    那么下面的代码会打印出所有可能的值:

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

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

    如果您需要支持 Swift 3.x 或 4.0,您可以通过添加以下代码来模仿 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
    

    【讨论】:

    • @DmitryPetukhov 我很乐意提供帮助,但是:(1) 你确定你有最新版本的代码吗? (一些崩溃是在一个月前修复的)和 (2) 请提供一个可以重现崩溃的自定义类型的 MCVE,以及您的 Xcode 版本。
    • 这对我来说很适合调试版本,但是一旦我创建一个版本并上传到 TestFlight,它就会崩溃。 Apple 是否以某种方式剥离了这一点?
    • 看来你的版本比内置版本的 CaseIterator 有一个积极的一面。我可以使用您的版本扩展在另一个文件中定义的枚举。如果您将扩展中的 allCases 公开,您还可以扩展在不同框架中定义的枚举。
    • @Cyber​​Mew 我已经更新了答案以澄清它。 Swift 书籍和 Xcode 10 发行说明中提到 CaseIterable 是我的回答之后的,他们使用了简化的示例,其中枚举不支持 String,而不是当前的 Stack Overflow 问题。
    • 我想强调“# if !swift (>=4.2)”的重要性。如果您在 swift 4.2 之前编写代码并且忘记删除下面的代码“# if !swift (>=4.2)”,那么当您使用 Xcode 版本 11.4 在您的测试设备上本地构建时,一切都会好起来的。但是,当您从应用商店或试飞中下载您的应用时,该段代码会使您的应用崩溃。这种错误很难发现或调试。
    【解决方案7】:

    抱歉,我的回答是针对我如何在我需要做的事情中使用这篇文章的。对于那些偶然发现这个问题的人,正在寻找一种方法来find枚举中的一个案例,这就是这样做的方法(Swift 2 中的新功能):

    编辑:小写 camelCase 现在是 Swift 3 枚举值的标准

    // From apple docs: If the raw-value type is specified as String and you don’t assign values to the cases explicitly, each unassigned case is implicitly assigned a string with the same text as the name of that case.
    
    enum Theme: String
        {
        case white, blue, green, lavender, grey
        }
    
    func loadTheme(theme: String)
        {
        // this checks the string against the raw value of each enum case (note that the check could result in a nil value, since it's an optional, which is why we introduce the if/let block
        if let testTheme = Theme(rawValue: theme)
            {
            // testTheme is guaranteed to have an enum value at this point
            self.someOtherFunction(testTheme)
            }
        }
    

    对于那些想知道枚举枚举的人,此页面上给出的答案包括一个包含所有枚举值数组的静态 var/let 是正确的。最新的 Apple tvOS 示例代码包含这种完全相同的技术。

    话虽如此,他们应该在语言中构建一个更方便的机制(Apple,你在听吗?)!

    【讨论】:

      【解决方案8】:

      有一个聪明的方法,但令人沮丧的是,它说明了两种不同类型的枚举之间的区别。

      试试这个:

          func makeDeck() -> Card[] {
            var deck: Card[] = []
            var suits: Suit[] = [.Hearts, .Diamonds, .Clubs, .Spades]
            for i in 1...13 {
              for suit in suits {
                deck += Card(rank: Rank.fromRaw(i)!, suit: suit)
              }
            }
            return deck
          }
      

      交易是由数字(原始值)支持的枚举隐式显式排序,而不受数字支持的枚举显式隐式无序。

      例如当我们给枚举值数字时,语言足够狡猾,可以弄清楚数字的顺序。 另一方面,如果我们不给它任何排序,当我们尝试迭代这些值时,语言会举起手来“是的,但是你想先去哪一个?? ?”

      其他可以做到这一点的语言(迭代无序的枚举)可能是相同的语言,其中所有的东西都在“底层”实际上是一个映射或字典,并且您可以迭代映射的键,无论是否有任何逻辑排序与否。

      所以诀窍是为它提供明确排序的东西,在这种情况下,数组中的西装实例按照我们想要的顺序排列。一旦你这么说,Swift 就像 “你为什么一开始不这么说呢?”

      另一个速记技巧是在 fromRaw 函数上使用强制运算符。这说明了关于枚举的另一个“陷阱”,即传入的可能值的范围通常大于枚举的范围。例如,如果我们说 Rank.fromRaw(60) 不会有返回值,所以我们使用了语言的 optional 特性,并且我们开始使用可选项的地方,很快就会出现强制. (或者 if let 结构对我来说仍然有点奇怪)

      【讨论】:

      • 另见 John 和 Andrew 的回复。
      • “交易是由数字(原始值)支持的枚举隐式显式排序,而不受数字支持的枚举显式隐式无序。” - 我不同意。每个枚举的“自然”顺序(无论它是否由整数支持)应该是声明枚举元素的顺序。通常这将与整数支持的枚举的原始值的顺序一致,但它不应该是必需的。
      • 我相信您可以考虑枚举由整数支持但不按原始值排序的情况。例如,橄榄球队的位置枚举。 int 值可以是球衣号码,但您可能不想按球衣号码对位置进行排序。对于那些不熟悉橄榄球的人来说,给定位置的球衣号码(传统上)是恒定的。例如,胡克的球衣号码是“2”,左翼的球衣号码是“11”。
      【解决方案9】:

      我花费的不仅仅是结构中的一种方法,就像 swift 书所要求的那样,但我在 enum 中设置了下一个函数。我会使用一个我不确定为什么的协议,但是将排名设置为 int 会搞砸。

      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.toRaw())
              }
          }
          mutating func next() -> Rank {
              var rank = self
              var rawrank = rank.toRaw()
              var nrank: Rank = self
              rawrank = rawrank + 1
              if let newRank = Rank.fromRaw(rawrank) {
                  println("\(newRank.simpleDescription())")
                  nrank = newRank
              } else {
                  return self
              }
              return nrank
          }
      }
      
      enum Suit {
          case Spades, Hearts, Diamonds, Clubs
          func color() -> String {
              switch self {
              case .Spades, .Clubs:
                  return "black"
              default:
                  return "red"
              }
          }
          func simpleDescription() -> String {
              switch self {
              case .Spades:
                  return "spades"
              case .Hearts:
                  return "hearts"
              case .Diamonds:
                  return "diamonds"
              case .Clubs:
                  return "clubs"
              }
          }
          mutating func next() -> Suit {
              switch self {
              case .Spades:
                  return Hearts
              case .Hearts:
                  return Diamonds
              case .Diamonds:
                  return Clubs
              case .Clubs:
                  return Spades
              }
          }
      }
      
      struct Card {
          var rank: Rank
          var suit: Suit
          func deck() -> Card[] {
              var tRank = self.rank
              var tSuit = self.suit
              let tcards = 52 // we start from 0
              var cards: Card[] = []
              for i in 0..tcards {
                  var card = Card(rank: tRank, suit: tSuit)
                  cards.append(card)
                  tRank = tRank.next()
                  tSuit = tSuit.next()
              }
              return cards
          }
          func simpleDescription() -> String {
              return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
          }
      }
      
      var card = Card(rank: .Ace, suit: .Spades)
      var deck = card.deck()
      

      我使用了一些一般知识,但可以通过将花色乘以等级来轻松纠正(如果您不使用标准的纸牌,并且您必须相应地更改枚举,如果基本上只是逐步完成不同的枚举。为了节省时间,我使用了排名rawValues,如果你愿意,你可以对西装做同样的事情。但是,这个例子没有它,所以我决定在不改变西装的情况下弄清楚rawValue

      【讨论】:

        【解决方案10】:

        我的解决方案是声明一个包含所有enum 可能性的数组。所以for循环可以遍历所有这些。

        //Function inside struct Card
        static func generateFullDeck() -> [Card] {
            let allRanks = [Rank.Ace, Rank.Two, Rank.Three, Rank.Four, Rank.Five, Rank.Six, Rank.Seven, Rank.Eight, Rank.Nine, Rank.Ten, Rank.Jack, Rank.Queen, Rank.King]
            let allSuits = [Suit.Hearts, Suit.Diamonds, Suit.Clubs, Suit.Spades]
            var myFullDeck: [Card] = []
        
            for myRank in allRanks {
                for mySuit in allSuits {
                    myFullDeck.append(Card(rank: myRank, suit: mySuit))
                }
            }
            return myFullDeck
        }
        
        //actual use:
        let aFullDeck = Card.generateFullDeck()    //Generate the desired full deck
        
        var allDesc: [String] = []
        for aCard in aFullDeck {
            println(aCard.simpleDescription())    //You'll see all the results in playground
        }
        

        【讨论】:

          【解决方案11】:

          我使用了以下方法,假设我知道Rankenum中的最后一个值,并且所有等级在Ace之后都有增量值

          我更喜欢这种方式,因为它干净、小巧、易于理解

           func cardDeck() -> Card[] {
               var cards: Card[] = []
               let minRank = Rank.Ace.toRaw()
               let maxRank = Rank.King.toRaw()
          
               for rank in minRank...maxRank {
                   if var convertedRank: Rank = Rank.fromRaw(rank) {
                       cards.append(Card(rank: convertedRank, suite: Suite.Clubs))
                       cards.append(Card(rank: convertedRank, suite: Suite.Diamonds))
                       cards.append(Card(rank: convertedRank, suite: Suite.Hearts))
                       cards.append(Card(rank: convertedRank, suite: Suite.Spades))
                   }
              }
          
              return cards
          }
          

          【讨论】:

            【解决方案12】:

            这是我用来迭代 enum 并从一个 enum 提供多个值类型的方法

            enum IterateEnum: Int {
                case Zero
                case One
                case Two
                case Three
                case Four
                case Five
                case Six
                case Seven
            
                //tuple allows multiple values to be derived from the enum case, and
                //since it is using a switch with no default, if a new case is added,
                //a compiler error will be returned if it doesn't have a value tuple set
                var value: (french: String, spanish: String, japanese: String) {
                    switch self {
                    case .Zero: return (french: "zéro", spanish: "cero", japanese: "nuru")
                    case .One: return (french: "un", spanish: "uno", japanese: "ichi")
                    case .Two: return (french: "deux", spanish: "dos", japanese: "ni")
                    case .Three: return (french: "trois", spanish: "tres", japanese: "san")
                    case .Four: return (french: "quatre", spanish: "cuatro", japanese: "shi")
                    case .Five: return (french: "cinq", spanish: "cinco", japanese: "go")
                    case .Six: return (french: "six", spanish: "seis", japanese: "roku")
                    case .Seven: return (french: "sept", spanish: "siete", japanese: "shichi")
                    }
                }
            
                //Used to iterate enum or otherwise access enum case by index order.
                //Iterate by looping until it returns nil
                static func item(index: Int) -> IterateEnum? {
                    return IterateEnum.init(rawValue: index)
                }
            
                static func numberFromSpanish(number: String) -> IterateEnum? {
                    return findItem { $0.value.spanish == number }
                }
            
                //use block to test value property to retrieve the enum case        
                static func findItem(predicate: ((_: IterateEnum) -> Bool)) -> IterateEnum? {
            
                    var enumIndex: Int = -1
                    var enumCase: IterateEnum?
            
                    //Iterate until item returns nil
                    repeat {
                        enumIndex += 1
                        enumCase = IterateEnum.item(index: enumIndex)
            
                        if let eCase = enumCase {
            
                            if predicate(eCase) {
                                return eCase
                            }
                        }
                    } while enumCase != nil
                    return nil
                }
            }
            
            var enumIndex: Int = -1
            var enumCase: IterateEnum?
            
            // Iterate until item returns nil
            repeat {
                enumIndex += 1
                enumCase = IterateEnum.item(index: enumIndex)
                if let eCase = enumCase {
                    print("The number \(eCase) in french: \(eCase.value.french), spanish: \(eCase.value.spanish), japanese: \(eCase.value.japanese)")
                }
            } while enumCase != nil
            
            print("Total of \(enumIndex) cases")
            
            let number = IterateEnum.numberFromSpanish(number: "siete")
            
            print("siete in japanese: \((number?.value.japanese ?? "Unknown"))")
            

            这是输出:

            数字零法语:zéro,西班牙语:cero,日语:nuru
            第一名 法语:un,西班牙语:uno,日语:ichi
            数字二法语:deux,西班牙语:dos,日语:ni
            数字三法语:trois,西班牙语:tres,日语:san
            数字四法语:quatre,西班牙语:cuatro,日语:shi
            数字五法语:cinq,西班牙语:cinco,日语:go
            数字六法语:六,西班牙语:seis,日语:roku
            七号法语:sept,西班牙语:siete,日语:shichi

            共8例

            日文:shichi


            更新

            我最近创建了一个协议来处理枚举。该协议需要一个带有 Int 原始值的枚举:

            protocol EnumIteration {
            
                //Used to iterate enum or otherwise access enum case by index order. Iterate by looping until it returns nil
            
                static func item(index:Int) -> Self?
                static func iterate(item:((index:Int, enumCase:Self)->()), completion:(()->())?) {
                static func findItem(predicate:((enumCase:Self)->Bool)) -> Self?
                static func count() -> Int
            }
            
            extension EnumIteration where Self: RawRepresentable, Self.RawValue == Int {
            
                //Used to iterate enum or otherwise access enum case by index order. Iterate by looping until it returns nil
                static func item(index:Int) -> Self? {
                    return Self.init(rawValue: index)
                }
            
                static func iterate(item:((index:Int, enumCase:Self)->()), completion:(()->())?) {
            
                    var enumIndex:Int = -1
                    var enumCase:Self?
            
                    //Iterate until item returns nil
                    repeat {
                        enumIndex += 1
                        enumCase = Self.item(enumIndex)
            
                        if let eCase = enumCase {
                            item(index: enumIndex, enumCase: eCase)
                        }
                    } while enumCase != nil
                    completion?()
                }
            
                static func findItem(predicate:((enumCase:Self)->Bool)) -> Self? {
            
                    var enumIndex:Int = -1
                    var enumCase:Self?
            
                    //Iterate until item returns nil
                    repeat {
                        enumIndex += 1
                        enumCase = Self.item(enumIndex)
            
                        if let eCase = enumCase {
            
                            if predicate(enumCase:eCase) {
                                return eCase
                            }
                        }
                    } while enumCase != nil
                    return nil
                }
            
                static func count() -> Int {
                    var enumIndex:Int = -1
                    var enumCase:Self?
            
                    //Iterate until item returns nil
                    repeat {
                        enumIndex += 1
                        enumCase = Self.item(enumIndex)
                    } while enumCase != nil
            
                    //last enumIndex (when enumCase == nil) is equal to the enum count
                    return enumIndex
                }
            }
            

            【讨论】:

              【解决方案13】:

              我使用计算属性来完成它,它返回所有值的数组(感谢这篇文章http://natecook.com/blog/2014/10/loopy-random-enum-ideas/)。但是,它也使用 int 原始值,但我不需要在单独的属性中重复枚举的所有成员。

              更新 Xcode 6.1 稍微改变了如何使用rawValue 获取枚举成员,所以我修复了列表。还修复了第一个错误的小错误rawValue

              enum ValidSuits: Int {
                  case Clubs = 0, Spades, Hearts, Diamonds
                  func description() -> String {
                      switch self {
                      case .Clubs:
                          return "♣︎"
                      case .Spades:
                          return "♠︎"
                      case .Diamonds:
                          return "♦︎"
                      case .Hearts:
                          return "♥︎"
                      }
                  }
              
                  static var allSuits: [ValidSuits] {
                      return Array(
                          SequenceOf {
                              () -> GeneratorOf<ValidSuits> in
                              var i=0
                              return GeneratorOf<ValidSuits> {
                                  return ValidSuits(rawValue: i++)
                              }
                          }
                      )
                  }
              }
              

              【讨论】:

                【解决方案14】:

                我添加了函数count(),并迭代了值:

                public enum MetricType: Int {
                    case mvps = 0
                    case allNBA = 1
                    case championshipRings = 2
                    case finalAppearances = 3
                    case gamesPlayed = 4
                    case ppg = 5
                
                    static func count() -> Int {
                        return (ppg.rawValue) + 1
                    }
                
                    static var allValues: [MetricType] {
                        var array: [MetricType] = Array()
                        var item : MetricType = MetricType.mvps
                        while item.rawValue < MetricType.count() {
                            array.append(item)
                            item = MetricType(rawValue: (item.rawValue + 1))!
                        }
                    return array
                    }
                }
                

                【讨论】:

                  【解决方案15】:

                  这是我建议的方法。它并不完全令人满意(我对 Swift 和 OOP 很陌生!)但也许有人可以改进它。这个想法是让每个枚举提供自己的范围信息作为.first.last 属性。它只为每个枚举添加了两行代码:仍然有点硬编码,但至少它没有复制整个集合。它确实需要将 Suit 枚举修改为 Int,就像 Rank 枚举一样,而不是无类型。

                  这里是我添加到 . 枚举中的代码,而不是回显整个解决方案,位于 case 语句之后(Suit 枚举类似):

                  var first: Int { return Ace.toRaw() }
                  var last: Int { return King.toRaw() }
                  

                  以及我用来将卡片组构建为字符串数组的循环。 (问题定义没有说明该套牌的结构。)

                  func createDeck() -> [String] {
                      var deck: [String] = []
                      var card: String
                      for r in Rank.Ace.first...Rank.Ace.last {
                          for s in Suit.Hearts.first...Suit.Hearts.last {
                              card = Rank.simpleDescription( Rank.fromRaw(r)!)() + " of " + Suit.simpleDescription( Suit.fromRaw(s)!)()
                             deck.append( card)
                         }
                      }
                      return deck
                  }
                  

                  这是不令人满意的,因为属性关联到一个元素而不是枚举。但它确实增加了“for”循环的清晰度。我想说Rank.first 而不是Rank.Ace.first。它工作(与任何元素),但它是丑陋的。有人可以展示如何将其提升到枚举级别吗?

                  为了让它发挥作用,我将 createDeck 方法从 Card 结构中取出。我无法弄清楚如何从该结构返回一个 [String] 数组,而且无论如何放置这样一个方法似乎是一个不好的地方。

                  【讨论】:

                    【解决方案16】:

                    枚举有toRaw()fromRaw() 方法。所以如果你的原始值是Int,你可以从第一个到最后一个enum进行迭代:

                    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"
                            }
                        }
                    }
                    
                    for i in Suit.Spades.toRaw()...Suit.Clubs.toRaw() {
                        if let covertedSuit = Suit.fromRaw(i) {
                            let description = covertedSuit.simpleDescription()
                        }
                    }
                    

                    一个问题是您需要在运行simpleDescription 方法之前测试可选值,因此我们首先将convertedSuit 设置为我们的值,然后将一个常量设置为convertedSuit.simpleDescription()

                    【讨论】:

                    • 最初的问题是关于字符串类型枚举而不是 Int
                    【解决方案17】:

                    此解决方案在可读性和可维护性之间取得了适当的平衡。

                    struct Card {
                    
                        // ...
                    
                        static func deck() -> Card[] {
                            var deck = Card[]()
                            for rank in Rank.Ace.toRaw()...Rank.King.toRaw() {
                                for suit in [Suit.Spades, .Hearts, .Clubs, .Diamonds] {
                                    let card = Card(rank: Rank.fromRaw(rank)!, suit: suit)
                                    deck.append(card)
                                }
                            }
                        return deck
                        }
                    }
                    
                    let deck = Card.deck()
                    

                    【讨论】:

                    • 在我看来,这是最好的解决方案。当我看到 swift 代码时,大多数情况下,可读性并不比 objc 好。但是,如果程序员更加关注他们代码的读者,情况可能会如此。他们未来的自己,例如 :)
                    【解决方案18】:

                    Swift 5 解决方案:

                    enum Suit: String, CaseIterable {
                        case spades = "♠"
                        case hearts = "♥"
                        case diamonds = "♦"
                        case clubs = "♣"
                    }
                    
                    // access cases like this:
                    
                    for suitKey in Suit.allCases {
                        print(suitKey)
                    }
                    

                    【讨论】:

                      【解决方案19】:

                      更新到 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
                              }
                          }
                      }
                      

                      将代码更新为 Swift 2.2 表单 @Kametrixom's answer

                      对于 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
                              }
                          }
                      }
                      

                      【讨论】:

                      • @silvansky 你能解释一下你的意思吗?
                      • 哎呀,对不起,我重新检查了,有一个游乐场错误:在实际项目中,此代码按预期工作,谢谢! =)
                      • 很好的解决方案!此外,在 Swift 3 上也能正常工作,只需少量更改('AnyGenerator' 重命名为 'AnyIterator' 和 '.memory' 重命名为 '.pointee')。
                      【解决方案20】:

                      在 Swift 3 中,当底层枚举具有 rawValue 时,您可以实现 Strideable 协议。优点是不会像其他一些建议那样创建值数组,并且标准的 Swift“for in”循环可以工作,这是一种很好的语法。

                      // "Int" to get rawValue, and Strideable so we can iterate
                      enum MyColorEnum: Int, Strideable {
                          case Red
                          case Green
                          case Blue
                          case Black
                      
                          // required by Strideable
                          typealias Stride = Int
                      
                          func advanced(by n:Stride) -> MyColorEnum {
                              var next = self.rawValue + n
                              if next > MyColorEnum.Black.rawValue {
                                  next = MyColorEnum.Black.rawValue
                              }
                              return MyColorEnum(rawValue: next)!
                          }
                      
                          func distance(to other: MyColorEnum) -> Int {
                              return other.rawValue - self.rawValue
                          }
                      
                          // just for printing
                          func simpleDescription() -> String {
                              switch self {
                              case .Red: return "Red"
                              case .Green: return "Green"
                              case .Blue: return "Blue"
                              case .Black: return "Black"
                              }
                          }
                      }
                      
                      // this is how you use it:
                      for i in MyColorEnum.Red ... MyColorEnum.Black {
                          print("ENUM: \(i)")
                      }
                      

                      【讨论】:

                      • 啊,正是我在寻找替换 ForwardIndexType 的东西。现在我的迭代在使用站点上看起来不错......只是正确的 Swifty 方式。
                      【解决方案21】:

                      你可以尝试这样枚举

                      enum Planet: String {
                          case Mercury
                          case Venus
                          case Earth
                          case Mars
                      
                          static var enumerate: [Planet] {
                              var a: [Planet] = []
                              switch Planet.Mercury {
                                  case .Mercury: a.append(.Mercury); fallthrough
                                  case .Venus: a.append(.Venus); fallthrough
                                  case .Earth: a.append(.Earth); fallthrough
                                  case .Mars: a.append(.Mars)
                              }
                          return a
                          }
                      }
                      
                      Planet.enumerate // [Mercury, Venus, Earth, Mars]
                      

                      【讨论】:

                      • 那是一大堆没用的代码!它相当于static var enumerate = [Mercury, Venus, Earth, Mars],与投票最多的答案stackoverflow.com/a/24137319/1033581 相比,它是一个低于标准的答案
                      • @Cœur 这个答案有一个重要的好处是使用编译器来保证你不会错过任何一个案例。
                      • @Cœur 有同样的问题,允许你犯用户错误,即如果你写return [Mercury, Venus, Mars]而不是return [Mercury, Venus, Earth, Mars],编译器不会抱怨
                      • @dchakarov 为了清楚起见,我决定发布改进作为答案:stackoverflow.com/a/50409525/1033581
                      • @Cœur 如果在你的新答案中你用这个return [.spades, .hearts, .clubs] 替换return 语句,编译器不会说什么,然后当你尝试在代码中使用它时,你会得到[TestApp.Suit.spades, TestApp.Suit.hearts, TestApp.Suit.clubs] - 这就是我的观点 - 如果您正在处理一个大枚举并且您必须不时添加或删除案例,您的解决方案很容易出现遗漏错误,而当前的答案虽然不简洁,但更安全。跨度>
                      【解决方案22】:

                      编辑: Swift Evolution Proposal SE-0194 Derived Collection of Enum Cases 针对这个问题提出了一个冷静的解决方案。我们在 Swift 4.2 及更新版本中看到了它。该提案还指出了一些workarounds,它们与这里已经提到的一些类似,但仍然可能会很有趣。

                      为了完整起见,我也会保留我原来的帖子。


                      这是基于@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)
                      }
                      

                      【讨论】:

                        【解决方案23】:

                        假设您不对枚举的情况使用原始值分配,原则上可以这样做:

                        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())")
                        }
                        

                        【讨论】:

                        • 这很好,但它只适用于从 0 开始的连续整数枚举
                        • @Robert,正如我在上面的评论中所说:“你不使用原始值分配枚举的情况”
                        • 是的 - 不要使用原始值,以及将基础类型设置为 int。在swift中,您不需要像西装示例中那样的枚举类型。 enum ItWontWorkForThisEnum {case a, b, c}
                        • 如果元组与枚举案例相关联,这将如何解决问题?
                        • 你不能很容易地将元组与枚举关联起来。
                        【解决方案24】:

                        其他解决方案有效,但它们都做出了假设,例如可能的等级和花色的数量,或者第一个和最后一个等级可能是什么。诚然,在可预见的未来,一副纸牌的布局可能不会发生太大变化。然而,总的来说,编写尽可能少的假设的代码会更整洁。我的解决方案:

                        我已将原始类型添​​加到 Suit 枚举,因此我可以使用 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 语句中展开并检查其值,无需假设 RankSuit 案例的数量:

                        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 类型枚举,但我想知道如何迭代其他类型(字符串、自定义类型等)。
                        • 这绝对是最好的解决方案。需要注意的一件事。在本书的示例中,它没有 sdduursma 所具有的“case Spades = 1”。起初我没有抓住这个。这是一种选择,或者您可以只使用“var m = 0”
                        • 这假设原始值是连续的。当这不正确时,例如当枚举表示位掩码标志时,循环会过早退出。
                        • 此解决方案假定您可以修改Suit 的定义。在这个例子中你可以,但是这个练习的目的是让你使用enums,就像它们来自外部一样。
                        • 我唯一要抱怨的是,我不能把它作为静态方法调用,而必须先创建一个卡片对象。
                        【解决方案25】:

                        Xcode 10 与 Swift 4.2

                        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)
                        

                        打印:

                        [“薪水”、“经验”、“技术”、“未使用”、“未使用的高价值”]

                        【讨论】:

                          【解决方案26】:

                          这是一篇相当老的帖子,来自 Swift 2.0。现在这里有一些更好的解决方案,它们使用了 swift 3.0 的新特性: Iterating through an Enum in Swift 3.0

                          在这个问题上,有一个解决方案使用 Swift 4.2 的新功能(在我编写此编辑时尚未发布): How do I get the count of a Swift enum?


                          在这个线程和其他线程中有很多好的解决方案,但是其中一些非常复杂。我喜欢尽可能地简化。这是一个可能适用于不同需求的解决方案,但我认为它在大多数情况下效果很好:

                          enum Number: String {
                              case One
                              case Two
                              case Three
                              case Four
                              case EndIndex
                          
                              func nextCase () -> Number
                              {
                                  switch self {
                                  case .One:
                                      return .Two
                                  case .Two:
                                      return .Three
                                  case .Three:
                                      return .Four
                                  case .Four:
                                      return .EndIndex
                          
                                  /* 
                                  Add all additional cases above
                                  */
                                  case .EndIndex:
                                      return .EndIndex
                                  }
                              }
                          
                              static var allValues: [String] {
                                  var array: [String] = Array()
                                  var number = Number.One
                          
                                  while number != Number.EndIndex {
                                      array.append(number.rawValue)
                                      number = number.nextCase()
                                  }
                                  return array
                              }
                          }
                          

                          迭代:

                          for item in Number.allValues {
                              print("number is: \(item)")
                          }
                          

                          【讨论】:

                          • 这感觉像是很多工作都特定于您创建的单个枚举 - 我不确定返回 [Number.One.rawValue, Number.Two.rawValue, ...]在这种情况下,它并不干净。
                          • 这是一篇相当老的帖子,来自 Swift 2.0。现在这里有一些更好的解决方案,它们使用了 swift 3.0 的新功能:stackoverflow.com/questions/41352594/… 在这个问题上,有一个解决方案使用了(在我编写此编辑时尚未发布)Swift 4.2 的新功能:@987654324 @
                          【解决方案27】:

                          (对 Karthik Ku​​mar 答案的改进)

                          此解决方案使用编译器来保证您不会错过任何案例。

                          enum Suit: String {
                              case spades = "♠"
                              case hearts = "♥"
                              case diamonds = "♦"
                              case clubs = "♣"
                          
                              static var enumerate: [Suit] {
                                  switch Suit.spades {
                                  // make sure the two lines are identical ^_^
                                  case        .spades, .hearts, .diamonds, .clubs:
                                      return [.spades, .hearts, .diamonds, .clubs]
                                  }
                              }
                          }
                          

                          【讨论】:

                            【解决方案28】:

                            这篇文章在这里是相关的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。并不像人们希望的那样优雅。
                            • 同意“但是”......但是正如文章中所述,也许存在一个问题,即枚举实际上是一个集合,因此是无序的......请注意......订单案例定义在不会是一个糟糕的开始!
                            • 在 Java 中编译器会为你做这件事,也许 Swift 2.0 也会这样做。特别是在 Java 中,所有枚举都有一个描述(Java 中的 toString)方法,该方法将字符串作为案例名称(Washers,...),并自动创建一组案例。 Java 还为您提供位置索引。正如我所说,也许是 Swift 2.0。
                            • 理想情况下,您将拥有类似于 c# 实现的东西,您可以在其中执行 Enum.Values(typeof(FooEnum)) 但作为扩展方法(如 map 或 reduce)公开。 FooEnum.values() :: values(EnumType -&gt; [EnumType])
                            • 这篇文章在最后提出了一个很好的观点,即通过单元测试使每个枚举值都位于 allValues 数组中。但是,我仍然可以看到有人添加了更多元素,但没有在单元测试中强制执行它们,这仍然让我们回到开头,不确定所有枚举值都保存在 allValues 中。
                            【解决方案29】:

                            我发现了一种有点 hacky 感觉但更安全的方法,它不需要输入两次值或引用枚举值的内存,因此不太可能中断。

                            基本上,不是使用枚举,而是使用单个实例创建结构,并使所有枚举值成为常量。然后可以使用Mirror

                            查询变量
                            public struct Suit{
                            
                                // the values
                                let spades = "♠"
                                let hearts = "♥"
                                let diamonds = "♦"
                                let clubs = "♣"
                            
                                // make a single instance of the Suit struct, Suit.instance
                                struct SStruct{static var instance: Suit = Suit()}
                                static var instance : Suit{
                                    get{return SStruct.instance}
                                    set{SStruct.instance = newValue}
                                }
                            
                                // an array with all of the raw values
                                static var allValues: [String]{
                                    var values = [String]()
                            
                                    let mirror = Mirror(reflecting: Suit.instance)
                                    for (_, v) in mirror.children{
                                        guard let suit = v as? String else{continue}
                                        values.append(suit)
                                    }
                            
                                    return values
                                }
                            }
                            

                            如果您使用此方法,要获取单个值,您需要使用 Suit.instance.clubsSuit.instance.spades

                            但所有这些都太无聊了……让我们做一些事情,让它更像一个真正的枚举!

                            public struct SuitType{
                            
                                // store multiple things for each suit
                                let spades = Suit("♠", order: 4)
                                let hearts = Suit("♥", order: 3)
                                let diamonds = Suit("♦", order: 2)
                                let clubs = Suit("♣", order: 1)
                            
                                struct SStruct{static var instance: SuitType = SuitType()}
                                static var instance : SuitType{
                                    get{return SStruct.instance}
                                    set{SStruct.instance = newValue}
                                }
                            
                                // a dictionary mapping the raw values to the values
                                static var allValuesDictionary: [String : Suit]{
                                    var values = [String : Suit]()
                            
                                    let mirror = Mirror(reflecting: SuitType.instance)
                                    for (_, v) in mirror.children{
                                        guard let suit = v as? Suit else{continue}
                                        values[suit.rawValue] = suit
                                    }
                            
                                    return values
                                }
                            }
                            
                            public struct Suit: RawRepresentable, Hashable{
                                public var rawValue: String
                                public typealias RawValue = String
                            
                                public var hashValue: Int{
                                    // find some integer that can be used to uniquely identify
                                    // each value. In this case, we could have used the order
                                    // variable because it is a unique value, yet to make this
                                    // apply to more cases, the hash table address of rawValue
                                    // will be returned, which should work in almost all cases
                                    // 
                                    // you could also add a hashValue parameter to init() and
                                    // give each suit a different hash value
                                    return rawValue.hash
                                }
                            
                                public var order: Int
                                public init(_ value: String, order: Int){
                                    self.rawValue = value
                                    self.order = order
                                }
                            
                                // an array of all of the Suit values
                                static var allValues: [Suit]{
                                    var values = [Suit]()
                            
                                    let mirror = Mirror(reflecting: SuitType.instance)
                                    for (_, v) in mirror.children{
                                        guard let suit = v as? Suit else{continue}
                                        values.append(suit)
                                    }
                            
                                    return values
                                }
                            
                                // allows for using Suit(rawValue: "♦"), like a normal enum
                                public init?(rawValue: String){
                                    // get the Suit from allValuesDictionary in SuitType, or return nil if that raw value doesn't exist
                                    guard let suit = SuitType.allValuesDictionary[rawValue] else{return nil}
                                    // initialize a new Suit with the same properties as that with the same raw value
                                    self.init(suit.rawValue, order: suit.order)
                                }
                            }
                            

                            你现在可以做类似的事情

                            let allSuits: [Suit] = Suit.allValues
                            

                            for suit in Suit.allValues{
                               print("The suit \(suit.rawValue) has the order \(suit.order)")
                            }
                            

                            但是,要获得单曲,您仍然需要使用 SuitType.instance.spadesSuitType.instance.hearts。为了让这更直观一点,您可以向Suit 添加一些代码,让您可以使用Suit.type.* 而不是SuitType.instance.*

                            public struct Suit: RawRepresentable, Hashable{
                               // ...your code...
                            
                               static var type = SuitType.instance
                            
                               // ...more of your code...
                            }
                            

                            您现在可以使用Suit.type.diamonds 代替SuitType.instance.diamonds,或使用Suit.type.clubs 代替SuitType.instance.clubs

                            【讨论】:

                              【解决方案30】:

                              另一种解决方案:

                              enum Suit: String {
                                  case spades = "♠"
                                  case hearts = "♥"
                                  case diamonds = "♦"
                                  case clubs = "♣"
                              
                                  static var count: Int {
                                      return 4   
                                  }
                              
                                  init(index: Int) {
                                      switch index {
                                          case 0: self = .spades
                                          case 1: self = .hearts
                                          case 2: self = .diamonds
                                          default: self = .clubs
                                      }
                                  }
                              }
                              
                              for i in 0..<Suit.count {
                                  print(Suit(index: i).rawValue)
                              }
                              

                              【讨论】:

                                猜你喜欢
                                • 1970-01-01
                                • 2018-02-14
                                • 1970-01-01
                                • 2022-06-11
                                • 1970-01-01
                                • 2021-11-16
                                • 1970-01-01
                                相关资源
                                最近更新 更多