将枚举大小写检查写入不符合Equatable一致性的Bool变量

时间:2020-05-11 12:21:23

标签: swift enums

我有一个带有与此相关的值的枚举:

enum SomeState {
    case loggedOut(redirectAfterLogin: Module? = nil)
    case loggedIn
}

现在,在某些情况下,我想精确比较两个状态(例如都注销,并且重定向目标是否相等),有时我只想知道它们是否都注销了。我的问题与第二种情况有关:我想检查状态是否已注销并将其写入变量,但是显然我不能实现Equatable来解决一般情况忽略参数。

一种实现此目的的方法是在Enum本身上实现一个计算属性isLoggedOut,但是由于这里只是一个示例,而我的实际代码要大得多,因此这对我来说不是一个选择。 / p>

第二种方式(我当前使用的方式)是:

func whatever() {
    if case .loggedOut = self.state {
        self.isLoggedOut = true
    } else {
        self.isLoggedOut = false
    }
}

这可行,但我宁愿这样写:

func whatever() {
    self.isLoggedOut = (case .loggedOut = self.state)
}

我遗漏了一些东西吗,还是真的不可能直接将if子句的大小写比较写成变量(或类似的单行解决方案)?

3 个答案:

答案 0 :(得分:2)

您只需要将whatever更改为计算属性而不是函数,将对isLoggedOut的赋值修改为return语句,便得到了{{1} }属性。

isLoggedOut

简化的示例代码,其中包含var isLoggedOut: Bool { if case .loggedOut = self.state { return true } else { return false } } 属性的类型也定义了state属性:

isLoggedOut

答案 1 :(得分:1)

如何?

enum SomeState {
    case loggedOut(redirectAfterLogin: Int? = nil)
    case loggedIn

    var isLoggedOut: Bool {
        switch self {
        case .loggedOut(_):  return true
        default: return false
        }
    }
}

var aState: SomeState = .loggedOut()

if aState.isLoggedOut {
    print("logged out")
} else {
    print("not logged out")
}

(我将关联的值redirectAfterLogin切换为简单的标量Int,因此它将为我们这些没有您的模块类型定义的人进行编译。您需要将其切换回。)

这里的窍门是计算所得的属性isLoggedOut,该属性使用switch语句忽略针对LoggedOut情况的关联值。

答案 2 :(得分:0)

我认为不可能避免在SomeState之前使用.loggedOut。即便如此,它还是比其他答案中的语言内置选项更好。

最好将ModuleSomeState设为Equatable,但这不是必须的。

public extension Mirror {
  /// Get the associated value from an `enum` instance.
  func getAssociatedValue<AssociatedValue>(
    _: AssociatedValue.Type = AssociatedValue.self
  ) -> AssociatedValue? {
    guard let childValue = children.first?.value
    else { return nil }

    if let associatedValue = childValue as? AssociatedValue {
      return associatedValue
    }

    let labeledAssociatedValue = Mirror(reflecting: childValue).children.first
    return labeledAssociatedValue?.value as? AssociatedValue
  }
}
/// Match `enum` cases with associated values, while disregarding the values themselves.
/// - Parameter makeCase: Looks like `Enum.case`.
public func ~= <Enum: Equatable, AssociatedValue>(
  makeCase: (AssociatedValue) -> Enum,
  instance: Enum
) -> Bool {
  Mirror(reflecting: instance).getAssociatedValue().map(makeCase)
  == instance
}

/// Match `enum` cases with associated values, while disregarding the values themselves.
/// - Parameter makeCase: Looks like `Enum.case`.
public func ~= <Enum, AssociatedValue>(
  makeCase: (AssociatedValue) -> Enum,
  instance: Enum
) -> Bool {
  let instanceMirror = Mirror(reflecting: instance)

  guard let dummyCase = instanceMirror.getAssociatedValue().map(makeCase)
  else { return false }

  return
    Mirror(reflecting: dummyCase).children.first?.label
    == instanceMirror.children.first?.label
}
struct ? {
  enum SomeState {
    struct Module { }

    case loggedOut(redirectAfterLogin: Module? = nil)
    case loggedIn
  }

  var isLoggedOut: Bool
  var state: SomeState

  mutating func whatever() {
    isLoggedOut = SomeState.loggedOut ~= state
  }
}

更多用法示例:

enum ?: Equatable {
  case tuple(cat: String, hat: String)
  case labeled(cake: String)
  case noAssociatedValue
}

let tupleCase = ?.tuple(cat: "?", hat: "?")
XCTAssertTrue(?.tuple ~= tupleCase)

XCTAssertTrue( ?.labeled ~= ?.labeled(cake: "?") )

let makeTupleCase = ?.tuple
XCTAssertFalse(makeTupleCase ~= ?.noAssociatedValue)

switch tupleCase {
case ?.labeled: XCTFail()
case makeTupleCase: break
default: XCTFail()
}