我有这个枚举:
enum Foo {
case a(x: Int)
case b(x: Int)
case c
case d
}
和foo
let foo = Foo.a(x: 10)
我想检查foo
是 a
,b
还是c
,无论x
是什么。
使用switch语句,我可以做到:
switch foo {
case .a, .b, .c:
...
case .d:
break
}
但是有点长。
我认为我可以对if case
做同样的事情:
if case .a, .b, .c = foo { ... }
这产生了编译器错误。
然后我找到了this question,并尝试了此操作:
if [Foo.a, Foo.b, Foo.c].contains(foo) { ... }
编译器认为该数组的类型为[Any]
,所以这也不起作用...
除了将其提取为方法并调用该方法外,我还能做什么? Swift 4.2中有什么新功能可以解决此问题?
答案 0 :(得分:1)
Swift不支持此操作,因为Foo
实例需要模式匹配,因为它们不是Equatable
。并且唯一允许多个模式匹配的分隔符是,
,并且该运算符对应于and
操作,而您不能使用or
。
一种丑陋(我会说是不正确或误导性的)方法是对Equatable
增添一致性,而忽略相关的值:
enum Foo: Equatable {
case a(x: Int)
case b(x: Int)
case c
case d
static func ==(_ lhs: Foo, _ rhs: Foo) -> Bool {
switch (lhs, rhs) {
case (.a, .a): return true
case (.b, .b): return true
case (.c, .c): return true
case (.d, .d): return true
default: return false
}
}
}
然后您可以执行以下操作:
if [Foo.a(x: 0), Foo.b(x: 0), Foo.c].contains(foo) { ... }
另一种方法是添加一个index
属性,并在测试时使用该属性:
enum Foo {
case a(x: Int)
case b(x: Int)
case c
case d
var index: Int {
switch self {
case .a: return 0
case .b: return 1
case .c: return 2
case .d: return 3
}
}
}
并按照
使用if [Foo.a(x: 0), Foo.b(x: 0), Foo.c].map({ $0.index }).contains(foo.index) { ... }
这两种解决方案都比简单的开关更为冗长,只有在需要多次使用它们的情况下,它们才是可行的。
或者,您可以使用以下内容扩展Array
:
extension Array where Element == Foo {
func matchesCase(_ foo: Foo) -> Bool {
return contains {
switch ($0, foo) {
case (.a, .a): return true
case (.b, .b): return true
case (.c, .c): return true
case (.d, .d): return true
default: return false
}
}
}
}
,并像这样使用它:
if [Foo.a(x: 0), Foo.b(x: 0), Foo.c].matchesCase(foo) { ... }
第四个解决方案:)。添加sameCase
函数:
enum Foo {
case a(x: Int)
case b(x: Int)
case c
case d
func sameCase(_ foo: Foo) -> Bool {
switch self {
// a little bit more verbose, but don't risk missing new cases
case .a: if case .a = foo { return true } else { return false }
case .b: if case .b = foo { return true } else { return false }
case .c: if case .c = foo { return true } else { return false }
case .d: if case .d = foo { return true } else { return false }
}
}
}
用法:
if [Foo.a(x: 0), Foo.b(x: 0), Foo.c].contains(where: foo.sameCase) { ... }
// or
if foo.sameCase(.a(x: 0)) || foo.sameCase(.b(x: 0)) || foo.sameCase(.c) { ... }
答案 1 :(得分:0)
如果您打算在多个地方多次重复进行此测试,那么一遍又一遍地复制冗长的版本确实很烦人;但是,您只需将这段代码封装在扩展中即可。
extension Foo {
var isABorC: Bool {
switch self {
case .a, .b, .c:
return true
default:
return false
}
}
}
所以现在您的测试变成这样:
if foo.isABorC { ... }
或者您可以简单地使其成为枚举声明的一部分:
enum Foo {
case a(x: Int)
case b(x: Int)
case c
case d
var isABorC: Bool {
switch self {
case .a, .b, .c:
return true
case .d:
return false
}
}
}
有一个例子是swift(4.2)文档,该文档使用嵌套枚举实现一副纸牌的等级,其中可以添加isFaceCard
变量。
最重要的是:您不必无休止地重复这段文字 ad nauseam 。您可以隐藏它,直到找到更优雅的解决方案为止。
答案 2 :(得分:-3)
不幸的是,没有其他方法。
这是因为Foo.a的类型为(Int)-> Foo。之所以不能使用array.contains,是因为Closure和Foo是不同的类型,因此编译器假定您想要的是Any数组。
要亲自查看此现象,请尝试以下代码:
print(type(of: Foo.a))
您将获得(Int) -> Foo
。