Swift 4.2引入了新的CaseIterable
协议,该协议会自动为枚举中的所有情况生成一个数组属性。
现在,我想为Enum继承自CaseIterable
的Enum实现一个默认方法,该方法可以返回给定案例的下一个案例。如果这种情况是最后一种情况,则返回第一种情况。像个圆圈。
如果我为特定的Enum编写此代码,它将正常工作:
enum Direction: CaseIterable {
case east, south, west, north
func next() -> Direction {
let all = type(of: self).allCases // 1
if self == all.last! {
return all.first!
} else {
let index = all.firstIndex(of: self)!
return all[index + 1]
}
}
}
print(Direction.east.next()) // south
print(Direction.north.next()) // east
但是我想对许多枚举实现此功能。重复复制和粘贴代码效果不好(更不用说每个Enum都完全相同)。
所以我尝试了这个。但是出了点问题。
(我建议您将以下代码复制到操场上,以便您可以更快地理解此问题):
extension CaseIterable {
func next() -> Self {
let all = type(of: self).allCases // 1
if self == all.last { // 2
return all.first!
} else {
let index = all.firstIndex { (ele) -> Bool in
self == ele // 3
}
return all[index + 1]
}
}
}
三点:
all
的类型为Self.AllCases
,是Collection
类型。但是在上面的方法中,它是[Direction]
。Value of type 'Self.AllCases' has no member 'last'
(即使我避免使用last
,也无法避免第3行的错误。)Binary operator '==' cannot be applied to two 'Self' operands
即使我使用通用约束,也是如此。
func next<T: CaseIterable>(element: T) -> T {...}
有解决方案吗? :)
答案 0 :(得分:6)
您的方法存在一些问题:
Collection
协议未定义last
属性。==
进行比较,它们必须为Equatable
。index(after:)
。这似乎是一个可行的解决方案(已通过Xcode 10.0 beta 2测试):
extension CaseIterable where Self: Equatable {
func next() -> Self {
let all = Self.allCases
let idx = all.index(of: self)!
let next = all.index(after: idx)
return all[next == all.endIndex ? all.startIndex : next]
}
}
示例:
enum Direction: CaseIterable {
case east, south, west, north
}
print(Direction.east.next()) // south
print(Direction.north.next()) // east
备注:
CaseIterable
,并且
那些也是Equatable
(但是编译器不知道
通过它自己)。因此Self: Equatable
不是
真正的限制。Self.allCases
可在Swift 4.2中用于访问type属性
通过实例方法。allCases
的元素。enum Direction: CaseIterable
之所以编译,是因为具体
enum Direction
类型为Equatable
,其Direction.allCases
是Array
–具有整数索引和last
属性。