Swift 4.2引入了一个新的CaseIterable协议,它自动生成枚举中所有情况的数组属性. 现在我想实现Enum的默认方法继承自CaseIterable,它可以返回给定案例的下一个案例.如果这种情况是最后一种情
现在我想实现Enum的默认方法继承自CaseIterable,它可以返回给定案例的下一个案例.如果这种情况是最后一种情况,请返回第一种情况.像一个圆圈.
如果我为特定的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] } } }
三点:
>所有类型都是Self.AllCases,这是一种集合类型.但是在上面的方法中,它是[Direction].
>第2行出现错误,说’Self.AllCases’类型的值没有成员’last’
(即使我避免使用最后一次,也无法避免第3行的错误.)
>在第3行,错误是二元运算符’==’不能应用于两个’Self’操作数
甚至我使用通用约束,它也是一样的.
func next<T: CaseIterable>(element: T) -> T {...}
有解决方案吗:)
您的方法存在一些问题:> Collection协议未定义最后一个属性.
>为了将元素与==进行比较,它们必须是Equatable.
>集合索引不一定是整数,它们必须递增
索引(后:).
这似乎是一个有效的解决方案(使用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不是真正的限制.>可以在Swift 4.2中使用Self.allCases来访问type属性来自实例方法.>强制解包是安全的,因为我们知道价值是allCases的一个元素.>你的枚举方向:CaseIterable编译,因为具体枚举方向类型是Equatable,它的Direction.allCases是一个数组 – 它有整数索引和最后一个属性.