如何确定Swift enum中的案例数?
(我希望避免手动枚举所有值,或者如果可能的话使用旧的“enum_count技巧”。)
如何确定Swift enum中的案例数?
(我希望避免手动枚举所有值,或者如果可能的话使用旧的“enum_count技巧”。)
当前回答
为什么要把事情搞得这么复杂?Int enum最简单的计数器是添加:
病例数
最后。和…维奥拉,现在你有计数了,又快又简单
其他回答
它可以使用一个静态常量,其中包含枚举的最后一个值加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()
}
}
}
我为自己解决了这个问题,创建了一个协议(EnumIntArray)和一个全局实用函数(EnumIntArray),可以很容易地将“All”变量添加到任何enum(使用swift 1.2)。“all”变量将包含枚举中所有元素的数组,因此您可以使用all。算一算
它只适用于使用Int类型原始值的枚举,但也许它可以为其他类型提供一些启发。
它还解决了我在上面和其他地方读到的“编号差距”和“迭代时间过多”问题。
其思想是将EnumIntArray协议添加到枚举,然后通过调用EnumIntArray函数定义一个“all”静态变量,并为其提供第一个元素(如果编号有间隙,则为最后一个元素)。
因为静态变量只初始化了一次,所以遍历所有原始值的开销只会对程序造成一次冲击。
示例(无空白):
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)解决方案将是以下(只有当你的enum是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,否则我推荐这个解决方案。
对于我的用例,在一个代码库中,多人可以向一个枚举添加键,这些情况都应该在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的开销
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。