swift - 如何在 Swift 中为 CaseIterable 枚举实现“下一个”属性
问题描述
我正在尝试将next
var 添加到枚举中。我可以为特定的枚举这样做,但想对其进行一般扩展,以便我可以通过指定带有协议的枚举来从枚举值中获取“下一个”枚举案例,例如CaseNextIterable
enum MyEnum: CaseIterable { // 'next' here is possible thanks to 'CaseIterable' protocol
case a, b, c
// returns the next case, or first if at end of sequence
// ie. a.next == b, c.next == a
var next: Self {
var r: Self!
for c in Self.allCases + Self.allCases { // not efficient
if r != nil {
r = c
break
}
if c == self {
r = self
}
}
return r
}
}
解决方案
您可以将CaseIterable
约束扩展Self
到Equatable
. 然后你只需要在firstIndex
你的CaseItareble
枚举之后找到索引并返回那个位置的元素。如果索引等于endIndex
所有情况中的第一个,则返回第一个元素。
extension CaseIterable where Self: Equatable {
private var allCases: AllCases { Self.allCases }
var next: Self {
let index = allCases.index(after: allCases.firstIndex(of: self)!)
guard index != allCases.endIndex else { return allCases.first! }
return allCases[index]
}
}
另一种选择是约束AllCases
到BidirectionalCollection
. 这将允许您获取枚举的最后一个元素,检查它是否等于 self 并返回第一个元素,而无需迭代整个集合:
extension CaseIterable where Self: Equatable, AllCases: BidirectionalCollection {
var allCases: AllCases { Self.allCases }
var next: Self {
guard allCases.last != self else { return allCases.first! }
return allCases[allCases.index(after: allCases.firstIndex(of: self)!)]
}
}
扩展 CaseIterable 下一个和上一个属性:
extension CaseIterable {
typealias Index = AllCases.Index
var first: Self { allCases.first! }
private var allCases: AllCases { Self.allCases }
private static func index(after i: Index) -> Index { allCases.index(after: i) }
}
extension CaseIterable where AllCases: BidirectionalCollection {
var last: Self { allCases.last! }
private static func index(before i: Index) -> Index { allCases.index(before: i) }
}
extension CaseIterable where Self: Equatable {
var index: Index { Self.firstIndex(of: self) }
private static func firstIndex(of element: Self) -> Index { allCases.firstIndex(of: element)! }
}
extension CaseIterable where Self: Equatable, AllCases: BidirectionalCollection {
var previous: Self { first == self ? last : allCases[Self.index(before: index)] }
var next: Self { last == self ? first : allCases[Self.index(after: index)] }
}
游乐场测试;
enum Enum: CaseIterable {
case a,b,c
}
let value: Enum = .c
let next = value.next // a
let next2 = next.next // b
let next3 = next2.next // c
let previous = value.previous // b
let previous2 = previous.previous // a
let previous3 = previous2.previous // c
推荐阅读
- database - Spring Boot - Liquibase 不创建 DATABASECHANGELOG 表
- actionscript-3 - 设置 XML .@
基于interator AS3 - spring-boot - 在 Spring Boot 2 + Spring Security 5 应用程序中结合基于表单和基于 oauth2 的登录提供程序
- jquery - 苗条的 php 到 jquery 发布失败未调用
- ruby-on-rails - Rails 5:用户(设计)和管理单个页面上的多个表
- .htaccess - .htaccess 从文件夹重定向到带有变量的文件
- javascript - html致敬和谷歌图表时间线中的svg问题 - 无法更改图表宽度
- ssl - 在 TLS 密码套件中哪里可以找到分组密码模式
- xmonad - 如何在 Xmonad 中仅为一个应用程序删除边框
- akka - 如何将密封特征解码为 JSON - Circe