Swift枚举类型包含存储字段?

3
所以,我已经花了大约2天的时间寻找这个问题的解决方案。我翻阅了Swift文档,尝试了许多不同的谷歌/堆栈溢出搜索,甚至随意编写代码,但都没有成功。我想问题的一部分是我不知道该如何表达它...所以如果这很困惑,对不起,但希望我附加的代码能够澄清它。
无论如何,我想知道是否可能在枚举中有一个私有字段,使得每个枚举值都有自己的该字段值。我不是在问如何扩展另一个类,并且我也不是在问关联值。不幸的是,我还没有找到任何在线信息说明Swift是否允许这样做,因此也不知道如何实现。为了强调我的问题,我正试图在Swift中重新创建以下Java代码:
// Java

public enum Direction {

  LEFT(0, -1), RIGHT(0, 1), UP(-1, 0), DOWN(1, 0);

  private final int rowChange, colChange;

  Direction(int rowChange, int colChange) {
    this.rowChange = rowChange;
    this.colChange = colChange;
  }

  public int rowChange() {
    return this.rowChange;
  }

  public int colChange() {
    return this.colChange;
  }

}

目前我已经有这个,它可以正常工作,但我更希望它具备存储值的功能,而不是通过每种可能情况进行切换。

// Swift

public enum Direction {

  case left, right, up, down

  public func rowChange() -> Int {
    switch self {
    case .left:
      return 0
    case .right:
      return 0
    case .up:
      return -1
    case .down:
      return 1
    }
  }

  public func colChange() -> Int {
    switch self {
    case .left:
      return -1
    case .right:
      return 1
    case .up:
      return 0
    case .down:
      return 0
    }
  }

}

无关的注释:你应该使用 var column: Int { ... } 而不是 func colChange() -> Int { ... }。由于这是一个 O(1) 操作,在 Swift 中编写它的事实标准方式是使用属性,而不是函数。 - Claus Jørgensen
可能是为什么Swift中的枚举具有计算属性而不具有存储属性?的重复问题。 - Guilherme Matuella
1
谢谢大家的反馈-我对Swift还比较新,所以很感激你们的帮助。我会将它们切换为计算属性... 在Java中我经常使用的一些东西在Swift中不能使用,这有点奇怪,但我想切换也可以。 - user11591069
4个回答

3

您可以符合 RawRepresentable 并使用 (Int, Int) 作为 Self.RawValue:

enum Direction : RawRepresentable {
    case left, right, up, down

    var rawValue : (Int, Int) {
        switch self {
        case .left: return (0, -1)
        case .right: return (0, 1)
        case .up: return (-1, 0)
        case .down: return (1, 0)
        }
    }

    init?(rawValue: (Int, Int)) {
        switch rawValue {
        case (0, -1): self = .left
        case (0, 1): self = .right
        case (-1, 0): self = .up
        case (1, 0): self = .down
        case (_, _): return nil
        }
    }

    var rowChange : Int { return self.rawValue.0 }
    var colChange : Int { return self.rawValue.1 }
}

然后,您可以像这样使用它:

print(Direction.left.rawValue) // --> (0, -1)
print(Direction.left.rowChange) // --> 0
print(Direction.left.colChange) // --> -1

2

使用self开关是标准的方法。您可以通过让枚举继承Int(或String是另一种常见情况)来将单个值分配给枚举。但对于像这样的两个值,开关将是正常方式。

也许考虑使用结构体?像这样:


struct Direction: Equatable {

    let row: Int
    let column: Int

    static let left = Direction(row: 0, column: -1)
    static let right = Direction(row: 0, column: 1)
    static let up = Direction(row: -1, column: 0)
    static let down = Direction(row: 1, column: 0)

    private init() { 
        // Just to disable empty initialisation, not actually used.
        self.row = 0
        self.column = 0
    }

    private init(row: Int, column: Int) {
        self.row = row
        self.column = column
    }
}

"最初的回答"可以这样访问:

并且可以像这样进行访问:

let direction = Direction.left
let row = direction.row
let column = direction.column

此外,如果目标是使用模式匹配,当结构体继承自Equatable时,可以编写类似以下内容的代码: "最初的回答"。
switch direction {
case .left:
    break
case .right:
    break
case .up:
    break
case .down:
    break
default:
    break
}

1
使用“struct”方式的问题在于您可以使用其他值创建其他方向。您至少应该将“init”设置为私有,这样只有4个静态变量才可见。并且使两个属性为公共只读。 - rmaddy
@rmaddy提到了私有init的好处,但是列/行属性已经是只读的,所以我不确定你的意思。无论如何,我只是为OP提供了一个替代switch语句的选择。 - Claus Jørgensen
不用在意这两个属性。我短暂地忘记了它们由于是“let”而不可写。 - rmaddy
嗯,非常有趣 - 我甚至没有考虑过结构体的方法。谢谢你的建议! - user11591069

2
你的解决方案基本上是正确的想法,我只是建议避免使用原始类型偏执并返回一个(dX: Int, dY: Int)元组,或者甚至是像PointOffset结构体这样的东西。我还建议使用计算属性而不是无参数方法。
public enum Direction {

    case left, right, up, down

    public var offset: (dX: Int, dY: Int) {
        switch self {
        case .left:  return (dX: -1,  dY:  0)
        case .right: return (dX: +1,  dY:  0)
        case .up:    return (dX:  0,  dY: +1)
        case .down:  return (dX:  0,  dY: -1)
        }
    }
}

如果您预计将需要不是单个距离单位的方向指示,那么我建议从enum切换到struct,并执行以下操作:

public struct PointOffset {
    public var dX, dY: Int

    static func left (by distance: Int) { return self.init(dX: -distance, dY: 0) }
    static func right(by distance: Int) { return self.init(dX: +distance, dY: 0) }
    static func up   (by distance: Int) { return self.init(dX: 0, dY: +distance) }
    static func down (by distance: Int) { return self.init(dX: 0, dY: -distance) }
}

1

self上使用switch将确保您为枚举中的每种情况返回有效值,并且如果您添加了新值到枚举中,它将保护您,因为那时您的代码将无法编译,直到您添加新值的情况。不要使用func,而是使用property:

public enum Direction {

    case left, right, up, down

    var rowChange: Int {
        switch self {
        case .left: return 0
        case .right: return 0
        case .up: return -1
        case .down: return 1
        }
    }

    var colChange: Int {
        switch self {
        case .left: return -1
        case .right: return 1
        case .up: return 0
        case .down: return 0
        }
    }

}

调用示例:

let list: [Direction] = [.left, .right, .up, .down]
for e in list {
    print("ROW changed = \(e.rowChange)")
    print("COL changed = \(e.colChange)")
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接