我如何得到一个Swift枚举的数量?

我怎样才能确定在一个Swift枚举的情况下的数量?

(我想避免手动枚举所有的值 ,或使用旧的“ enum_count技巧 ”,如果可能的话)。

我不知道任何通用的方法来计数枚举案件的数量。 然而,我注意到枚举个案的hashValue属性是增量式的,从零开始,顺序由声明的顺序决定。 所以,最后一个枚举加上一个散列对应的情况下的数量。

例如对于这个枚举:

 enum Test { case ONE case TWO case THREE case FOUR static var count: Int { return Test.FOUR.hashValue + 1} } 

count返回4。

我不能说,如果这是一个规则,或者如果它将来会改变,所以使用在你自己的风险 🙂

我有一个博客文章 ,更详细地介绍了这一点,但只要你的枚举的原始types是一个整数,你可以这样添加一个计数:

 enum Reindeer: Int { case Dasher, Dancer, Prancer, Vixen, Comet, Cupid, Donner, Blitzen case Rudolph static let count: Int = { var max: Int = 0 while let _ = Reindeer(rawValue: max) { max += 1 } return max }() } 

我定义了一个可重用的协议,根据Nate Cook发布的方法自动执行案例计数。

 protocol CaseCountable { static var caseCount: Int { get } } extension CaseCountable where Self: RawRepresentable, Self.RawValue == Int { internal static var caseCount: Int { var count = 0 while let _ = Self(rawValue: count) { count += 1 } return count } } 

那么我可以重用这个协议,例如如下:

 enum Planet : Int, CaseCountable { case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune } //.. print(Planet.caseCount) 

创build静态allValues数组,如此答案中所示

 enum ProductCategory : String { case Washers = "washers", Dryers = "dryers", Toasters = "toasters" static let allValues = [Washers, Dryers, Toasters] } ... let count = ProductCategory.allValues.count 

当你想枚举这些值时,这也是有用的,并且适用于所有的枚举types

如果实现没有任何反对使用整型枚举的方法,那么可以添加一个名为Count的额外成员值来表示枚举中的成员数 – 请参阅下面的示例:

 enum TableViewSections : Int { case Watchlist case AddButton case Count } 

现在你可以通过调用TableViewSections.Count.rawValue来获取枚举中的成员数,上面的例子将返回2。

当您在switch语句中处理枚举时,请确保在遇到Count成员时(在您不期望的情况下)抛出断言失败:

 func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { let currentSection: TableViewSections = TableViewSections.init(rawValue:section)! switch(currentSection) { case .Watchlist: return watchlist.count case .AddButton: return 1 case .Count: assert(false, "Invalid table view section!") } } 

这种function可以返回你的枚举数。

Swift 2

 func enumCount<T: Hashable>(_: T.Type) -> Int { var i = 1 while (withUnsafePointer(&i) { UnsafePointer<T>($0).memory }).hashValue != 0 { i += 1 } return i } 

Swift 3

 func enumCount<T: Hashable>(_: T.Type) -> Int { var i = 1 while (withUnsafePointer(to: &i, { return $0.withMemoryRebound(to: T.self, capacity: 1, { return $0.pointee }) }).hashValue != 0) { i += 1 } return i } 

这个函数依赖于2无证电stream (Swift 1.1) enum行为:

  • enum内存布局只是case的索引。 如果案例数从2到256,它是UInt8
  • 如果enum是从无效索引位处理的,则其hashValue值为0

所以请自担风险:)

 func enumCaseCount<T:Hashable>(t:T.Type) -> Int { switch sizeof(t) { case 0: return 1 case 1: for i in 2..<256 { if unsafeBitCast(UInt8(i), t).hashValue == 0 { return i } } return 256 case 2: for i in 257..<65536 { if unsafeBitCast(UInt16(i), t).hashValue == 0 { return i } } return 65536 default: fatalError("too many") } } 

用法:

 enum Foo:String { case C000 = "foo" case C001 = "bar" case C002 = "baz" } enumCaseCount(Foo) // -> 3 

带索引的string枚举

 enum eEventTabType : String { case Search = "SEARCH" case Inbox = "INBOX" case Accepted = "ACCEPTED" case Saved = "SAVED" case Declined = "DECLINED" case Organized = "ORGANIZED" static let allValues = [Search, Inbox, Accepted, Saved, Declined, Organized] var index : Int { return eEventTabType.allValues.indexOf(self)! } } 

计数: eEventTabType.allValues.count

索引: objeEventTabType.index

请享用 :)

嘿大家, unit testing呢?

 func testEnumCountIsEqualToNumberOfItemsInEnum() { var max: Int = 0 while let _ = Test(rawValue: max) { max += 1 } XCTAssert(max == Test.count) } 

这与Antonio的解决scheme相结合:

 enum Test { case one case two case three case four static var count: Int { return Test.four.hashValue + 1} } 

在主代码给你O(1)加上你会得到一个失败的testing,如果有人添加枚举案例five ,并不更新count的实现。

我写了一个简单的扩展,它给出了所有枚举的原始值是整数count属性:

 extension RawRepresentable where RawValue: IntegerType { static var count: Int { var i: RawValue = 0 while let _ = Self(rawValue: i) { i = i.successor() } return Int(i.toIntMax()) } } 

不幸的是,它给count属性的OptionSetType ,它将无法正常工作,所以这里是另一个版本,需要明确符合CaseCountable协议的任何枚举你想要计数:

 protocol CaseCountable: RawRepresentable {} extension CaseCountable where RawValue: IntegerType { static var count: Int { var i: RawValue = 0 while let _ = Self(rawValue: i) { i = i.successor() } return Int(i.toIntMax()) } } 

这与Tom Pelaia发布的方法非常相似,但适用于所有整数types。

当然,这不是dynamic的,但是对于很多用途,你可以通过添加到你的Enum的静态variables来获得

static var count: Int{ return 7 }

然后用它作为EnumName.count

对于我的用例,在一个代码库中,多个人可以将键添加到一个枚举中,并且这些情况都应该在allKeys属性中可用,所以必须针对枚举中的键validationallKeys。 这是为了避免有人忘记将密钥添加到所有密钥列表中。 将allKeys数组的计数(首先作为一个集合创build以避免模糊)与枚举中的键数进行匹配,可以确保它们全部存在。

上面的一些答案显示了在Swift 2中实现这一点的方法,但在Swift 3中没有任何工作。 这里是Swift 3格式化的版本:

 static func enumCount<T: Hashable>(_ t: T.Type) -> Int { var i = 1 while (withUnsafePointer(to: &i) { $0.withMemoryRebound(to:t.self, capacity:1) { $0.pointee.hashValue != 0 } }) { i += 1 } return i } static var allKeys: [YourEnumTypeHere] { var enumSize = enumCount(YourEnumTypeHere.self) let keys: Set<YourEnumTypeHere> = [.all, .your, .cases, .here] guard keys.count == enumSize else { fatalError("Missmatch between allKeys(\(keys.count)) and actual keys(\(enumSize)) in enum.") } return Array(keys) } 

根据您的使用情况,您可能只想在开发中运行testing,以避免在每个请求上使用allKeys的开销

如果你不想把你的代码放在最后一个枚举中,你可以在你的枚举中创build这个函数。

 func getNumberOfItems() -> Int { var i:Int = 0 var exit:Bool = false while !exit { if let menuIndex = MenuIndex(rawValue: i) { i++ }else{ exit = true } } return i } 

使用Inttypes枚举的Swift 3版本:

 protocol CaseCountable: RawRepresentable {} extension CaseCountable where RawValue == Int { static var count: RawValue { var i: RawValue = 0 while let _ = Self(rawValue: i) { i += 1 } return i } } 

积分:根据bzz和Nate Cook的答案。

通用的IntegerType (在Swift 3中重命名为Integer )不被支持,因为它是一个非常分散的通用types,缺乏很多的function。 successor不再适用于Swift 3。

请注意,Code Commander对Nate Cooks的回答仍然有效:

虽然很好,因为你不需要硬编码一个值,这将实例化每个枚举值每次调用。 那是O(n)而不是O(1)。

据我所知,目前没有解决方法,因为静态存储的属性不支持在genericstypes中使用这个作为协议扩展(而不是像Nate Cook做的每个枚举中实现)。

无论如何,对于小的枚举,这应该是没有问题的。 一个典型的用例就是Zorayr已经提到的UITableViewssection.count

你为什么这么复杂? Int enum的SIMPLEST计数器是添加:

case Count

到底。 而…中提琴 – 现在你有数 – 快速简单

扩展Matthieu Riegler的答案,这是一个Swift 3的解决scheme,不需要使用generics,并且可以使用EnumType.elementsCount使用枚举types轻松调用:

 extension RawRepresentable where Self: Hashable { // Returns the number of elements in a RawRepresentable data structure static var elementsCount: Int { var i = 1 while (withUnsafePointer(to: &i, { return $0.withMemoryRebound(to: self, capacity: 1, { return $0.pointee }) }).hashValue != 0) { i += 1 } return i } 

我通过创build一个协议(EnumIntArray)和一个全局实用函数(enumIntArray)为我自己解决了这个问题,使得向任何枚举(使用swift 1.2)添加一个“All”variables变得非常简单。 “all”variables将包含enum中所有元素的数组,因此您可以使用all.count来计数

它只适用于使用Inttypes的原始值的枚举,但也许可以为其他types提供一些灵感。

它还解决了我在上面和其他地方读过的“编号差距”和“迭代时间过长”的问题。

我们的想法是将EnumIntArray协议添加到枚举中,然后通过调用enumIntArray函数定义一个“all”静态variables,并为其提供第一个元素(如果编号中存在空格,则为最后一个元素)。

由于静态variables只是初始化一次,所有原始值的开销只会触发你的程序一次。

示例(没有间隙):

 enum Animals:Int, EnumIntArray { case Cat=1, Dog, Rabbit, Chicken, Cow static var all = enumIntArray(Animals.Cat) } 

示例(有差距):

 enum Animals:Int, EnumIntArray { case Cat = 1, Dog, case Rabbit = 10, Chicken, Cow static var all = enumIntArray(Animals.Cat, Animals.Cow) } 

以下是实现它的代码:

 protocol EnumIntArray { init?(rawValue:Int) var rawValue:Int { get } } func enumIntArray<T:EnumIntArray>(firstValue:T, _ lastValue:T? = nil) -> [T] { var result:[T] = [] var rawValue = firstValue.rawValue while true { if let enumValue = T(rawValue:rawValue++) { result.append(enumValue) } else if lastValue == nil { break } if lastValue != nil && rawValue > lastValue!.rawValue { break } } return result } 

这是微不足道的,但我认为一个更好的O(1)解决scheme将是以下( 只有如果你的枚举是Int开始在X等):

 enum Test : Int { case ONE = 1 case TWO case THREE case FOUR // if you later need to add additional enums add above COUNT so COUNT is always the last enum value case COUNT static var count: Int { return Test.COUNT.rawValue } // note if your enum starts at 0, some other number, etc. you'll need to add on to the raw value the differential } 

目前select的答案我仍然认为是所有枚举的最佳答案,除非你正在使用Int然后我推荐这个解决scheme。

 struct HashableSequence<T: Hashable>: SequenceType { func generate() -> AnyGenerator<T> { var i = 0 return AnyGenerator { let next = withUnsafePointer(&i) { UnsafePointer<T>($0).memory } if next.hashValue == i { i += 1 return next } return nil } } } extension Hashable { static func enumCases() -> Array<Self> { return Array(HashableSequence()) } static var enumCount: Int { return enumCases().enumCount } } enum E { case A case B case C } E.enumCases() // [A, B, C] E.enumCount // 3 

但要小心使用非枚举types。 一些解决方法可能是:

 struct HashableSequence<T: Hashable>: SequenceType { func generate() -> AnyGenerator<T> { var i = 0 return AnyGenerator { guard sizeof(T) == 1 else { return nil } let next = withUnsafePointer(&i) { UnsafePointer<T>($0).memory } if next.hashValue == i { i += 1 return next } return nil } } } extension Hashable { static func enumCases() -> Array<Self> { return Array(HashableSequence()) } static var enumCount: Int { return enumCases().count } } enum E { case A case B case C } Bool.enumCases() // [false, true] Bool.enumCount // 2 String.enumCases() // [] String.enumCount // 0 Int.enumCases() // [] Int.enumCount // 0 E.enumCases() // [A, B, C] E.enumCount // 4 

它可以使用包含枚举的最后一个值的静态常量加1。

 enum Color : Int { case Red, Orange, Yellow, Green, Cyan, Blue, Purple static let count: Int = Color.Purple.rawValue + 1 func toUIColor() -> UIColor{ switch self { case .Red: return UIColor.redColor() case .Orange: return UIColor.orangeColor() case .Yellow: return UIColor.yellowColor() case .Green: return UIColor.greenColor() case .Cyan: return UIColor.cyanColor() case .Blue: return UIColor.blueColor() case .Purple: return UIColor.redColor() } } }