如何确定Swift enum中的案例数?
(我希望避免手动枚举所有值,或者如果可能的话使用旧的“enum_count技巧”。)
如何确定Swift enum中的案例数?
(我希望避免手动枚举所有值,或者如果可能的话使用旧的“enum_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())
}
}
不幸的是,它给了计数属性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发布的方法非常相似,但适用于所有整数类型。
其他回答
Xcode 10更新
在枚举中采用CaseIterable协议,它提供了一个静态的allCases属性,其中包含所有枚举案例作为一个集合。只需使用它的count属性就可以知道枚举有多少个case。
请看马丁的答案(为他的答案而不是我的答案投票)
警告:下面的方法似乎不再有效。
我不知道有任何通用方法来计算枚举案例的数量。但是,我注意到枚举案例的hashValue属性是递增的,从零开始,顺序由声明案例的顺序决定。最后一个枚举加1的哈希值对应的是案例数。
例如,对于这个enum:
enum Test {
case ONE
case TWO
case THREE
case FOUR
static var count: Int { return Test.FOUR.hashValue + 1}
}
Count返回4。
我不能说这是一个规则,或者它在未来是否会改变,所以使用你自己的风险:)
如果实现不反对使用整数enum,你可以添加一个额外的成员值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!")
}
}
Swift 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)不受支持,因为它是一个严重碎片化的泛型类型,缺少很多函数。继任者在Swift 3中不再可用。
注意代码指挥官对Nate Cooks回答的注释仍然有效:
虽然这很好,因为您不需要硬编码一个值,但这会 每次调用枚举值时实例化每个枚举值。这是O(n) 而不是O(1)
据我所知,由于泛型类型中不支持静态存储属性,因此在使用此作为协议扩展时(并且没有像Nate Cook那样在每个枚举中实现)目前没有解决方案。
无论如何,对于小枚举,这应该不是问题。一个典型的用例就是section。如Zorayr所提到的UITableViews。
对于我的用例,在一个代码库中,多人可以向一个枚举添加键,这些情况都应该在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的开销
我有一篇博客文章详细介绍了这一点,但只要你的枚举的原始类型是一个整数,你可以这样添加一个计数:
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
}()
}