如何在Swift 4.2中获取下一个枚举(即编写循环方法)的案例

时间:2018-06-29 14:18:19

标签: swift enums

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]
    }
  }
}

三点:

  1. all的类型为Self.AllCases,是Collection类型。但是在上面的方法中,它是[Direction]
  2. 第2行显示错误Value of type 'Self.AllCases' has no member 'last' (即使我避免使用last,也无法避免第3行的错误。)
  3. 在第3行,错误为Binary operator '==' cannot be applied to two 'Self' operands

即使我使用通用约束,也是如此。

func next<T: CaseIterable>(element: T) -> T {...}

有解决方案吗? :)

1 个答案:

答案 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.allCasesArray –具有整数索引和last属性。