如何确定Swift枚举中的个案数?
(我想避免手动枚举所有值,或者尽可能避免使用旧的“ enum_count技巧 ”。)
如何确定Swift枚举中的个案数?
(我想避免手动枚举所有值,或者尽可能避免使用旧的“ enum_count技巧 ”。)
Answers:
从Swift 4.2(Xcode 10)开始,您可以声明符合CaseIterable
协议,这适用于所有没有关联值的枚举:
enum Stuff: CaseIterable {
case first
case second
case third
case forth
}
现在可以简单地通过
print(Stuff.allCases.count) // 4
有关更多信息,请参见
我有一篇博客文章,其中对此进行了更详细的介绍,但是只要您的枚举的原始类型是整数,就可以通过以下方式添加计数:
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
}()
}
case A=1, B=3
?
enum
具有Int
您忘记提及的原始值外,还有两个假设:具有Int原始值的Swift枚举不必从0开始(即使这是默认行为),并且它们的原始值可以是任意的,而不必增加1(即使这是默认行为)。
Xcode 10更新
CaseIterable
在枚举中采用协议,它提供了一个静态allCases
属性,其中包含所有枚举用例Collection
。只需使用其count
属性即可知道枚举有多少种情况。
请参阅马丁的答案作为示例(并认可他的答案,而不是我的答案)
警告:以下方法似乎不再起作用。
我不知道有任何通用方法来计算枚举案例的数量。但是,我注意到hashValue
枚举案例的属性是增量的,从零开始,并且顺序由声明案例的顺序确定。因此,最后一个枚举的哈希值加1对应于案例数。
例如,此枚举:
enum Test {
case ONE
case TWO
case THREE
case FOUR
static var count: Int { return Test.FOUR.hashValue + 1}
}
count
返回4。
我不能说这是否是规则,或者将来是否会改变,因此使用后果自负:)
hashValues
这些东西。我们所知道的是,它是一个随机的唯一值-将来可能会很容易更改,具体取决于某些编译器实现细节;但总体而言,缺少内置计数功能令人不安。
case ONE = 0
,则可以替换hashValue
为rawValue
。
static var count = 4
而不是将命运留在Swift的未来实现方式中
我定义了一个可重用的协议,该协议根据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)
count++
为,count+=1
因为++
注释将在Swift 3中删除
static var caseCount: Int { get }
吗?为什么需要static func
?
case A=1, B=3
?
0
没有空白。
创建静态allValues数组,如此答案所示
enum ProductCategory : String {
case Washers = "washers", Dryers = "dryers", Toasters = "toasters"
static let allValues = [Washers, Dryers, Toasters]
}
...
let count = ProductCategory.allValues.count
当您要枚举值时,这也很有用,并且适用于所有Enum类型
static let count = allValues.count
。然后,您可以allValues
根据需要将其设为私有。
如果实现不反对使用整数枚举,则可以添加一个额外的成员值,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!")
}
}
这种功能可以返回您的枚举计数。
斯威夫特2:
func enumCount<T: Hashable>(_: T.Type) -> Int {
var i = 1
while (withUnsafePointer(&i) { UnsafePointer<T>($0).memory }).hashValue != 0 {
i += 1
}
return i
}
迅捷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
}
enum
是也 Hashable
相同类型的。
带索引的字符串枚举
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
请享用 :)
哦,大家好,单元测试呢?
func testEnumCountIsEqualToNumberOfItemsInEnum() {
var max: Int = 0
while let _ = Test(rawValue: max) { max += 1 }
XCTAssert(max == Test.count)
}
结合安东尼奥的解决方案:
enum Test {
case one
case two
case three
case four
static var count: Int { return Test.four.hashValue + 1}
}
在主代码给你O(1)加上你会得到一个失败的测试,如果有人添加了一个枚举的情况下five
,不更新的实施count
。
此功能依赖于2种未记录的当前(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
我写了一个简单的扩展,为原始值是整数的所有枚举赋予一个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发布的方法非常相似,但适用于所有整数类型。
当然,它不是动态的,但是对于许多用途,您可以通过将静态var添加到Enum中来获得
static var count: Int{ return 7 }
然后将其用作 EnumName.count
enum EnumNameType: Int {
case first
case second
case third
static var count: Int { return EnumNameType.third.rawValue + 1 }
}
print(EnumNameType.count) //3
要么
enum EnumNameType: Int {
case first
case second
case third
case count
}
print(EnumNameType.count.rawValue) //3
*在Swift 4.2(Xcode 10)上可以使用:
enum EnumNameType: CaseIterable {
case first
case second
case third
}
print(EnumNameType.allCases.count) //3
对于我的用例,在一个代码库中,可能有多个人向枚举添加键,并且这些用例都应在allKeys属性中可用,因此必须针对枚举中的键验证allKeys,这一点很重要。这是为了避免有人忘记将其密钥添加到所有密钥列表中。将allKeys数组(首先创建为避免重复的集合)的计数与枚举中的键数进行匹配可确保它们全部存在。
上面的一些答案显示了在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)
}
根据您的用例,您可能只想在开发中运行测试,以避免在每个请求上使用allKeys的开销。
如果您不想将代码基于上一个枚举,则可以在枚举内创建此函数。
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
}
一个斯威夫特3版本的工作Int
类型枚举:
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
),因为它是一个分散的泛型类型,缺少许多功能。successor
不再适用于Swift 3。
请注意,从Code Commander到Nate Cooks答案的评论仍然有效:
不错,因为您不需要对值进行硬编码,但这会在每次调用它时实例化每个枚举值。那是O(n)而不是O(1)。
据我所知,由于泛型类型不支持静态存储的属性,因此,将其用作协议扩展时(并且未像Nate Cook那样在每个枚举中实现)目前尚无解决方法。
无论如何,对于小的枚举,这应该没有问题。如Zorayr所述,典型的用例是section.count
for UITableViews
。
扩展了Matthieu Riegler的答案,这是Swift 3的一种解决方案,不需要使用泛型,并且可以通过使用enum类型轻松调用EnumType.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
}
我自己创建了一个协议(EnumIntArray)和一个全局实用程序函数(enumIntArray),为自己解决了这个问题,可以很容易地向任何枚举添加“ All”变量(使用swift 1.2)。“ all”变量将包含枚举中所有元素的数组,因此您可以使用all.count进行计数
它仅适用于使用Int类型原始值的枚举,但也许可以为其他类型提供一些启发。
它还解决了我在上文和其他地方阅读的“编号间隔”和“迭代时间过多”的问题。
这个想法是将EnumIntArray协议添加到您的枚举中,然后通过调用enumIntArray函数定义一个“所有”静态变量,并为其提供第一个元素(如果编号中有空格,则提供最后一个元素)。
由于静态变量仅初始化一次,因此遍历所有原始值的开销只会对您的程序造成一次影响。
示例(无间隙):
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
}
以下方法来自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
}
然后,您只需要致电即可Weekdays.allValues.count
。
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
但请谨慎使用非枚举类型。一些解决方法可能是:
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
它可以使用包含枚举的最后一个值加一个的静态常量。
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()
}
}
}
这是次要的,但是我认为以下是更好的O(1)解决方案(仅当您的枚举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
}
我仍然相信当前选择的答案是所有枚举的最佳答案,除非您正在使用,否则Int
我建议使用此解决方案。
guard
一场反抗COUNT
,并抛出一个错误,返回false等,以解决您的类型的代表表示关注。