我如何得到一个快速枚举数?

时间:2022-07-16 16:22:29

How can I determine the number of cases in a Swift enum?

如何确定Swift enum中的案例数量?

(I would like to avoid manually enumerating through all the values, or using the old "enum_count trick" if possible.)

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

24 个解决方案

#1


13  

As of Swift 4.2 (latest developer snapshot) you can declare conformance to the CaseIterable protocol, this works for all enumerations without associated values:

在Swift 4.2(最新的开发人员快照)中,您可以声明对CaseIterable协议的一致性,这适用于所有没有关联值的枚举:

enum Stuff: CaseIterable {
    case first
    case second
    case third
    case forth
}

The number of cases is now simply obtained with

现在可以用简单的方法得到病例数

print(Stuff.allCases.count) // 4

For more information, see

有关更多信息,请参见

#2


127  

I have a blog post that goes into more detail on this, but as long as your enum's raw type is an integer, you can add a count this way:

我有一篇关于这方面更详细的博文,但只要枚举的原始类型是整数,就可以这样添加计数:

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

#3


82  

I'm not aware of any generic method to count the number of enum cases. I've noticed however that the hashValue property of the enum cases is incremental, starting from zero, and with the order determined by the order in which the cases are declared. So, the hash of the last enum plus one corresponds to the number of cases.

我不知道任何计算枚举数的通用方法。但是,我注意到enum用例的hashValue属性是递增的,从0开始,顺序由声明用例的顺序决定。所以,最后一次枚举的哈希值加上1对应的情况个数。

For example with this enum:

例如这个全会:

enum Test {
    case ONE
    case TWO
    case THREE
    case FOUR

    static var count: Int { return Test.FOUR.hashValue + 1}
}

count returns 4.

数返回4。

I cannot say if that's a rule or if it will ever change in the future, so use at your own risk :)

我不能说这是否是一条规则,或者它在未来是否会改变,所以你要自己承担风险:

#4


64  

I define a reusable protocol which automatically performs the case count based on the approach posted by Nate Cook.

我定义了一个可重用协议,该协议根据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
    }
}

Then I can reuse this protocol for example as follows:

然后,我可以重用该协议,例如:

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

#5


31  

Create static allValues array as shown in this answer

创建静态allValues数组,如图所示

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

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

...

let count = ProductCategory.allValues.count

This is also helpful when you want to enumerate the values, and works for all Enum types

当您希望枚举值并适用于所有枚举类型时,这也很有帮助

#6


16  

If the implementation doesn't have anything against using integer enums, you could add an extra member value called Count to represent the number of members in the enum - see example below:

如果实现不反对使用整数枚举,您可以添加一个名为Count的额外成员值来表示enum中的成员数量——请参见下面的示例:

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

Now you can get the number of members in the enum by calling, TableViewSections.Count.rawValue which will return 2 for the example above.

现在可以通过调用TableViewSections.Count来获取enum中的成员数。对于上面的示例,它将返回2。

When you're handling the enum in a switch statement, make sure to throw an assertion failure when encountering the Count member where you don't expect it:

当您在switch语句中处理enum时,请确保在遇到计数成员时抛出断言失败,而您并不期望它:

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

#7


13  

This kind of function is able to return the count of your enum.

这种函数可以返回枚举的计数。

Swift 2:

斯威夫特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:

斯威夫特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
   }

#8


9  

Oh hey everybody, what about unit tests?

大家好,单元测试呢?

func testEnumCountIsEqualToNumberOfItemsInEnum() {

    var max: Int = 0
    while let _ = Test(rawValue: max) { max += 1 }

    XCTAssert(max == Test.count)
}

This combined with Antonio's solution:

这与安东尼奥的解决方案相结合:

enum Test {

    case one
    case two
    case three
    case four

    static var count: Int { return Test.four.hashValue + 1}
}

in the main code gives you O(1) plus you get a failing test if someone adds an enum case five and doesn't update the implementation of count.

在主代码中给出了O(1)加上一个失败的测试,如果有人添加了enum案例5,并没有更新count的实现。

#9


8  

String Enum with Index

字符串枚举与指数

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

count : eEventTabType.allValues.count

数:eEventTabType.allValues.count

index : objeEventTabType.index

指数:objeEventTabType.index

Enjoy :)

享受:)

#10


7  

This function relies on 2 undocumented current(Swift 1.1) enum behavior:

此函数依赖于2个未记录的电流(Swift 1.1)枚举行为:

  • Memory layout of enum is just a index of case. If case count is from 2 to 256, it's UInt8.
  • enum的内存布局只是大小写的索引。如果case计数从2到256,它是UInt8。
  • If the enum was bit-casted from invalid case index, its hashValue is 0
  • 如果枚举来自无效的case索引,它的hashValue为0。

So use at your own risk :)

因此,请自行使用:

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

Usage:

用法:

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

#11


4  

Of course, it's not dynamic but for many uses you can get by with a static var added to your Enum

当然,它不是动态的,但是对于许多您可以通过在Enum中添加静态var而获得的使用

static var count: Int{ return 7 }

静态var计数:Int{return 7}

and then use it as EnumName.count

然后将它用作EnumName.count

#12


4  

I wrote a simple extension which gives all enums where raw value is integer a count property:

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

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

Unfortunately it gives the count property to OptionSetType where it won't work properly, so here is another version which requires explicit conformance to CaseCountable protocol for any enum which cases you want to count:

不幸的是,它将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())
    }
}

It's very similar to the approach posted by Tom Pelaia, but works with all integer types.

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

#13


2  

For my use case, in a codebase where multiple people could be adding keys to an enum, and these cases should all be available in the allKeys property, it's important that allKeys be validated against the keys in the enum. This is to avoid someone forgetting to add their key to the all keys list. Matching the count of the allKeys array(first created as a set to avoid dupes) against the number of keys in the enum ensures that they are all present.

对于我的用例,在一个代码基中,多个人员可以向enum添加键,而且这些情况都应该在allKeys属性中可用,因此在enum中根据键对所有键进行验证是很重要的。这是为了避免有人忘记将他们的密钥添加到所有密钥列表中。将allKeys数组的计数(首先创建为一个集合,以避免被欺骗)与enum中的键的数量匹配,可以确保它们都存在。

Some of the answers above show the way to achieve this in Swift 2 but none work in Swift 3. Here is the Swift 3 formatted version:

上面的一些答案显示了在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)
}

Depending on your use case, you might want to just run the test in development to avoid the overhead of using allKeys on each request

根据您的用例,您可能只想在开发中运行测试,以避免在每个请求上使用allkey的开销

#14


2  

Why do you make it all so complex? The SIMPLEST counter of Int enum is to add:

你为什么把一切都弄得这么复杂?Int enum最简单的计数器是:

case Count

病例数

In the end. And... viola - now you have the count - fast and simple

最后。和…维奥拉-现在你有计数-快速和简单

#15


1  

If you don't want to base your code in the last enum you can create this function inside your enum.

如果您不想在上一个enum中建立您的代码,您可以在enum中创建这个函数。

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
}

#16


1  

A Swift 3 version working with Int type enums:

使用Int类型枚举的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
    }
}

Credits: Based on the answers by bzz and Nate Cook.

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

Generic IntegerType (in Swift 3 renamed to Integer) is not supported, as it's a heavily fragmented generic type which lacks a lot of functions. successor is not available with Swift 3 anymore.

不支持泛型IntegerType(在Swift 3中改名为Integer),因为它是一种非常分散的泛型类型,缺少很多功能。Swift 3不再提供继承者。

Be aware that the comment from Code Commander to Nate Cooks answer is still valid:

请注意,代码指挥官对Nate Cooks的回答仍然有效:

While nice because you don't need to hardcode a value, this will instantiate every enum value each time it is called. That is O(n) instead of O(1).

虽然很好,因为您不需要硬编码一个值,但这将在每次调用时实例化每个enum值。这是O(n)而不是O(1)

As far as I know there is currently no workaround when using this as protocol extension (and not implementing in each enum like Nate Cook did) due to static stored properties not being supported in generic types.

据我所知,由于在泛型类型中不支持静态存储属性,所以在将它用作协议扩展时(而且没有像Nate Cook那样在每个枚举中实现),目前还没有解决方案。

Anyway, for small enums this should be no issue. A typical use case would be the section.count for UITableViews as already mentioned by Zorayr.

无论如何,对于小的枚举,这应该是没有问题的。一个典型的用例是section。根据Zorayr已经提到的UITableViews计算。

#17


1  

Extending Matthieu Riegler answer, this is a solution for Swift 3 that doesn't require the use of generics, and can be easily called using the enum type with EnumType.elementsCount:

扩展Matthieu Riegler的答案,这是Swift 3的一个解决方案,不需要使用泛型,并且可以使用枚举类型的enum类型轻松调用。elementscount:

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
}

#18


0  

I solved this problem for myself by creating a protocol (EnumIntArray) and a global utility function (enumIntArray) that make it very easy to add an "All" variable to any enum (using swift 1.2). The "all" variable will contain an array of all elements in the enum so you can use all.count for the count

我通过创建一个协议(EnumIntArray)和一个全局实用程序函数(EnumIntArray)来解决这个问题,该函数可以很容易地向任何enum(使用swift 1.2)添加“All”变量。“all”变量将包含枚举中所有元素的数组,以便您可以使用all。计数的计数

It only works with enums that use raw values of type Int but perhaps it can provide some inspiration for other types.

它只适用于使用Int类型的原始值的枚举,但它可能会为其他类型提供一些灵感。

It also addresses the "gap in numbering" and "excessive time to iterate" issues I've read above and elsewhere.

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

The idea is to add the EnumIntArray protocol to your enum and then define an "all" static variable by calling the enumIntArray function and provide it with the first element (and the last if there are gaps in the numbering).

其思想是向枚举中添加EnumIntArray协议,然后通过调用enguangtarray函数定义“all”静态变量,并为其提供第一个元素(如果编号中有空位,则为最后一个)。

Because the static variable is only initialized once, the overhead of going through all raw values only hits your program once.

因为静态变量只初始化一次,所以遍历所有原始值的开销只会影响程序一次。

example (without gaps) :

示例(没有缺陷):

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

example (with gaps) :

示例(空白):

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

Here's the code that implements it:

下面是实现它的代码:

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   
}

#19


0  

Or you can just define the _count outside the enum, and attach it statically:

或者,您可以在enum之外定义_count,并静态地附加它:

let _count: Int = {
    var max: Int = 0
    while let _ = EnumName(rawValue: max) { max += 1 }
    return max
}()

enum EnumName: Int {
    case val0 = 0
    case val1
    static let count = _count
}

That way no matter how many enums you create, it'll only ever be created once.

这样,无论你创建多少个枚举,它都只会被创建一次。

(delete this answer if static does that)

(如果是静态的,就删除这个答案)

#20


0  

I like the accepted answer. However for those who want to hardcode the count value but are using a enum type of String rather than Int, there is still the simple way of:

我喜欢公认的答案。然而,对于那些希望硬编码计数值但使用enum类型的字符串而不是Int的人来说,仍然有一种简单的方法:

enum Test: String {

case One = "One"
case Two = "Two"
case Three = "Three"
case Four = "Four"
case count = "4"


}

Access it

访问它

  guard let enumCount = Int(Test.count.rawValue) else { return } 

#21


0  

The following method comes from CoreKit and is similar to the answers some others have suggested. This works with Swift 4.

下面的方法来自CoreKit,类似于其他人的回答。这与Swift 4一起工作。

public protocol EnumCollection: Hashable {
    static func cases() -> AnySequence<Self>
    static var allValues: [Self] { get }
}

public extension EnumCollection {

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

    public static var allValues: [Self] {
        return Array(self.cases())
    }
}

enum Weekdays: String, EnumCollection {
    case sunday, monday, tuesday, wednesday, thursday, friday, saturday
}

Then you just need to just call Weekdays.allValues.count.

那么你只需要给工作日打个电话就行了。

#22


-1  

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

but be careful with usage on non-enum types. Some workaround could be:

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

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

#23


-1  

It can use a static constant which contains the last value of the enumeration plus one.

它可以使用一个静态常量,该常量包含枚举的最后一个值+ 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()
        }
    }
}

#24


-2  

This is minor, but I think a better O(1) solution would be the following (ONLY if your enum is Int starting at x, etc.):

这是次要的,但我认为更好的O(1)解决方案将是以下的(只有当您的enum从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 
}

The current selected answer I still believe is the best answer for all enums, unless you are working with Int then I recommend this solution.

目前所选的答案我仍然相信是所有枚举的最好答案,除非你正在使用Int,那么我推荐这个解决方案。

#1


13  

As of Swift 4.2 (latest developer snapshot) you can declare conformance to the CaseIterable protocol, this works for all enumerations without associated values:

在Swift 4.2(最新的开发人员快照)中,您可以声明对CaseIterable协议的一致性,这适用于所有没有关联值的枚举:

enum Stuff: CaseIterable {
    case first
    case second
    case third
    case forth
}

The number of cases is now simply obtained with

现在可以用简单的方法得到病例数

print(Stuff.allCases.count) // 4

For more information, see

有关更多信息,请参见

#2


127  

I have a blog post that goes into more detail on this, but as long as your enum's raw type is an integer, you can add a count this way:

我有一篇关于这方面更详细的博文,但只要枚举的原始类型是整数,就可以这样添加计数:

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

#3


82  

I'm not aware of any generic method to count the number of enum cases. I've noticed however that the hashValue property of the enum cases is incremental, starting from zero, and with the order determined by the order in which the cases are declared. So, the hash of the last enum plus one corresponds to the number of cases.

我不知道任何计算枚举数的通用方法。但是,我注意到enum用例的hashValue属性是递增的,从0开始,顺序由声明用例的顺序决定。所以,最后一次枚举的哈希值加上1对应的情况个数。

For example with this enum:

例如这个全会:

enum Test {
    case ONE
    case TWO
    case THREE
    case FOUR

    static var count: Int { return Test.FOUR.hashValue + 1}
}

count returns 4.

数返回4。

I cannot say if that's a rule or if it will ever change in the future, so use at your own risk :)

我不能说这是否是一条规则,或者它在未来是否会改变,所以你要自己承担风险:

#4


64  

I define a reusable protocol which automatically performs the case count based on the approach posted by Nate Cook.

我定义了一个可重用协议,该协议根据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
    }
}

Then I can reuse this protocol for example as follows:

然后,我可以重用该协议,例如:

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

#5


31  

Create static allValues array as shown in this answer

创建静态allValues数组,如图所示

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

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

...

let count = ProductCategory.allValues.count

This is also helpful when you want to enumerate the values, and works for all Enum types

当您希望枚举值并适用于所有枚举类型时,这也很有帮助

#6


16  

If the implementation doesn't have anything against using integer enums, you could add an extra member value called Count to represent the number of members in the enum - see example below:

如果实现不反对使用整数枚举,您可以添加一个名为Count的额外成员值来表示enum中的成员数量——请参见下面的示例:

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

Now you can get the number of members in the enum by calling, TableViewSections.Count.rawValue which will return 2 for the example above.

现在可以通过调用TableViewSections.Count来获取enum中的成员数。对于上面的示例,它将返回2。

When you're handling the enum in a switch statement, make sure to throw an assertion failure when encountering the Count member where you don't expect it:

当您在switch语句中处理enum时,请确保在遇到计数成员时抛出断言失败,而您并不期望它:

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

#7


13  

This kind of function is able to return the count of your enum.

这种函数可以返回枚举的计数。

Swift 2:

斯威夫特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:

斯威夫特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
   }

#8


9  

Oh hey everybody, what about unit tests?

大家好,单元测试呢?

func testEnumCountIsEqualToNumberOfItemsInEnum() {

    var max: Int = 0
    while let _ = Test(rawValue: max) { max += 1 }

    XCTAssert(max == Test.count)
}

This combined with Antonio's solution:

这与安东尼奥的解决方案相结合:

enum Test {

    case one
    case two
    case three
    case four

    static var count: Int { return Test.four.hashValue + 1}
}

in the main code gives you O(1) plus you get a failing test if someone adds an enum case five and doesn't update the implementation of count.

在主代码中给出了O(1)加上一个失败的测试,如果有人添加了enum案例5,并没有更新count的实现。

#9


8  

String Enum with Index

字符串枚举与指数

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

count : eEventTabType.allValues.count

数:eEventTabType.allValues.count

index : objeEventTabType.index

指数:objeEventTabType.index

Enjoy :)

享受:)

#10


7  

This function relies on 2 undocumented current(Swift 1.1) enum behavior:

此函数依赖于2个未记录的电流(Swift 1.1)枚举行为:

  • Memory layout of enum is just a index of case. If case count is from 2 to 256, it's UInt8.
  • enum的内存布局只是大小写的索引。如果case计数从2到256,它是UInt8。
  • If the enum was bit-casted from invalid case index, its hashValue is 0
  • 如果枚举来自无效的case索引,它的hashValue为0。

So use at your own risk :)

因此,请自行使用:

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

Usage:

用法:

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

#11


4  

Of course, it's not dynamic but for many uses you can get by with a static var added to your Enum

当然,它不是动态的,但是对于许多您可以通过在Enum中添加静态var而获得的使用

static var count: Int{ return 7 }

静态var计数:Int{return 7}

and then use it as EnumName.count

然后将它用作EnumName.count

#12


4  

I wrote a simple extension which gives all enums where raw value is integer a count property:

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

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

Unfortunately it gives the count property to OptionSetType where it won't work properly, so here is another version which requires explicit conformance to CaseCountable protocol for any enum which cases you want to count:

不幸的是,它将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())
    }
}

It's very similar to the approach posted by Tom Pelaia, but works with all integer types.

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

#13


2  

For my use case, in a codebase where multiple people could be adding keys to an enum, and these cases should all be available in the allKeys property, it's important that allKeys be validated against the keys in the enum. This is to avoid someone forgetting to add their key to the all keys list. Matching the count of the allKeys array(first created as a set to avoid dupes) against the number of keys in the enum ensures that they are all present.

对于我的用例,在一个代码基中,多个人员可以向enum添加键,而且这些情况都应该在allKeys属性中可用,因此在enum中根据键对所有键进行验证是很重要的。这是为了避免有人忘记将他们的密钥添加到所有密钥列表中。将allKeys数组的计数(首先创建为一个集合,以避免被欺骗)与enum中的键的数量匹配,可以确保它们都存在。

Some of the answers above show the way to achieve this in Swift 2 but none work in Swift 3. Here is the Swift 3 formatted version:

上面的一些答案显示了在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)
}

Depending on your use case, you might want to just run the test in development to avoid the overhead of using allKeys on each request

根据您的用例,您可能只想在开发中运行测试,以避免在每个请求上使用allkey的开销

#14


2  

Why do you make it all so complex? The SIMPLEST counter of Int enum is to add:

你为什么把一切都弄得这么复杂?Int enum最简单的计数器是:

case Count

病例数

In the end. And... viola - now you have the count - fast and simple

最后。和…维奥拉-现在你有计数-快速和简单

#15


1  

If you don't want to base your code in the last enum you can create this function inside your enum.

如果您不想在上一个enum中建立您的代码,您可以在enum中创建这个函数。

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
}

#16


1  

A Swift 3 version working with Int type enums:

使用Int类型枚举的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
    }
}

Credits: Based on the answers by bzz and Nate Cook.

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

Generic IntegerType (in Swift 3 renamed to Integer) is not supported, as it's a heavily fragmented generic type which lacks a lot of functions. successor is not available with Swift 3 anymore.

不支持泛型IntegerType(在Swift 3中改名为Integer),因为它是一种非常分散的泛型类型,缺少很多功能。Swift 3不再提供继承者。

Be aware that the comment from Code Commander to Nate Cooks answer is still valid:

请注意,代码指挥官对Nate Cooks的回答仍然有效:

While nice because you don't need to hardcode a value, this will instantiate every enum value each time it is called. That is O(n) instead of O(1).

虽然很好,因为您不需要硬编码一个值,但这将在每次调用时实例化每个enum值。这是O(n)而不是O(1)

As far as I know there is currently no workaround when using this as protocol extension (and not implementing in each enum like Nate Cook did) due to static stored properties not being supported in generic types.

据我所知,由于在泛型类型中不支持静态存储属性,所以在将它用作协议扩展时(而且没有像Nate Cook那样在每个枚举中实现),目前还没有解决方案。

Anyway, for small enums this should be no issue. A typical use case would be the section.count for UITableViews as already mentioned by Zorayr.

无论如何,对于小的枚举,这应该是没有问题的。一个典型的用例是section。根据Zorayr已经提到的UITableViews计算。

#17


1  

Extending Matthieu Riegler answer, this is a solution for Swift 3 that doesn't require the use of generics, and can be easily called using the enum type with EnumType.elementsCount:

扩展Matthieu Riegler的答案,这是Swift 3的一个解决方案,不需要使用泛型,并且可以使用枚举类型的enum类型轻松调用。elementscount:

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
}

#18


0  

I solved this problem for myself by creating a protocol (EnumIntArray) and a global utility function (enumIntArray) that make it very easy to add an "All" variable to any enum (using swift 1.2). The "all" variable will contain an array of all elements in the enum so you can use all.count for the count

我通过创建一个协议(EnumIntArray)和一个全局实用程序函数(EnumIntArray)来解决这个问题,该函数可以很容易地向任何enum(使用swift 1.2)添加“All”变量。“all”变量将包含枚举中所有元素的数组,以便您可以使用all。计数的计数

It only works with enums that use raw values of type Int but perhaps it can provide some inspiration for other types.

它只适用于使用Int类型的原始值的枚举,但它可能会为其他类型提供一些灵感。

It also addresses the "gap in numbering" and "excessive time to iterate" issues I've read above and elsewhere.

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

The idea is to add the EnumIntArray protocol to your enum and then define an "all" static variable by calling the enumIntArray function and provide it with the first element (and the last if there are gaps in the numbering).

其思想是向枚举中添加EnumIntArray协议,然后通过调用enguangtarray函数定义“all”静态变量,并为其提供第一个元素(如果编号中有空位,则为最后一个)。

Because the static variable is only initialized once, the overhead of going through all raw values only hits your program once.

因为静态变量只初始化一次,所以遍历所有原始值的开销只会影响程序一次。

example (without gaps) :

示例(没有缺陷):

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

example (with gaps) :

示例(空白):

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

Here's the code that implements it:

下面是实现它的代码:

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   
}

#19


0  

Or you can just define the _count outside the enum, and attach it statically:

或者,您可以在enum之外定义_count,并静态地附加它:

let _count: Int = {
    var max: Int = 0
    while let _ = EnumName(rawValue: max) { max += 1 }
    return max
}()

enum EnumName: Int {
    case val0 = 0
    case val1
    static let count = _count
}

That way no matter how many enums you create, it'll only ever be created once.

这样,无论你创建多少个枚举,它都只会被创建一次。

(delete this answer if static does that)

(如果是静态的,就删除这个答案)

#20


0  

I like the accepted answer. However for those who want to hardcode the count value but are using a enum type of String rather than Int, there is still the simple way of:

我喜欢公认的答案。然而,对于那些希望硬编码计数值但使用enum类型的字符串而不是Int的人来说,仍然有一种简单的方法:

enum Test: String {

case One = "One"
case Two = "Two"
case Three = "Three"
case Four = "Four"
case count = "4"


}

Access it

访问它

  guard let enumCount = Int(Test.count.rawValue) else { return } 

#21


0  

The following method comes from CoreKit and is similar to the answers some others have suggested. This works with Swift 4.

下面的方法来自CoreKit,类似于其他人的回答。这与Swift 4一起工作。

public protocol EnumCollection: Hashable {
    static func cases() -> AnySequence<Self>
    static var allValues: [Self] { get }
}

public extension EnumCollection {

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

    public static var allValues: [Self] {
        return Array(self.cases())
    }
}

enum Weekdays: String, EnumCollection {
    case sunday, monday, tuesday, wednesday, thursday, friday, saturday
}

Then you just need to just call Weekdays.allValues.count.

那么你只需要给工作日打个电话就行了。

#22


-1  

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

but be careful with usage on non-enum types. Some workaround could be:

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

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

#23


-1  

It can use a static constant which contains the last value of the enumeration plus one.

它可以使用一个静态常量,该常量包含枚举的最后一个值+ 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()
        }
    }
}

#24


-2  

This is minor, but I think a better O(1) solution would be the following (ONLY if your enum is Int starting at x, etc.):

这是次要的,但我认为更好的O(1)解决方案将是以下的(只有当您的enum从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 
}

The current selected answer I still believe is the best answer for all enums, unless you are working with Int then I recommend this solution.

目前所选的答案我仍然相信是所有枚举的最好答案,除非你正在使用Int,那么我推荐这个解决方案。