Перечисление (enum) — очень мощный тип в Swift. Я часто использую его в своем коде. Сегодня я собираюсь познакомить вас с некоторыми приемами перечисления в Swift, о которых вы могли не знать.
1. Сложные case с привязкой значений
Сложный case — это когда вы хотите сопоставить несколько кейсов вместе. Вы можете сделать это, написав шаблоны, разделенные запятыми, после регистра.
Ниже приведен пример из документации Swift. Объединяем каждый символ в два составных case гласный и согласный.
let someCharacter: Character = "e"
switch someCharacter {
case "a", "e", "i", "o", "u":
print("\(someCharacter) is a vowel")
case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
"n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
print("\(someCharacter) is a consonant")
default:
print("\(someCharacter) is not a vowel or a consonant")
}
Возможно, вы не знаете, что сложные случаи также работают с перечислениями со связанными значениями. Все шаблоны составного регистра должны включать один и тот же набор привязок значений, и каждая привязка должна получать значение одного и того же типа из всех шаблонов в составном регистре.
enum Job {
case developer(exp: Int)
case designer(exp: Int)
case pm(exp: Int)
}
let job = Job.developer(exp: 5)
switch job {
case .developer(let exp), .designer(let exp), .pm(let exp):
print("You have \(exp) year(s) of experience")
}
В приведенном выше примере будет распечатано количество лет независимо от должности.
Различное количество связанных значений
Один и тот же набор привязок значений не означает одинаковое количество связанных значений. Вы можете составлять варианты с разным количеством связанных значений, если вы связываете одно и то же количество значений.
Давайте посмотрим на это в действии. Я создал образец перечисления под названием Foo. Он содержит кейсы с разным количеством связанных значений и типов данных.
enum Foo {
case a(Int)
case b(Int, Int)
case c(Int, String, Int)
case d(String)
}
let test = Foo.c(1, "c", 3)
switch test {
case .a(let i), .b(_, let i), .c(let i, _, _):
print(i)
default:
break
}
вывод
1
Как видите, мы можем смешивать варианты с одним, двумя и тремя связанными значениями вместе, потому что мы связываем их с одинаковым количеством значений (один) и одним и тем же типом данных (Int).
Порядок
Порядок связанных значений не имеет значения. Просто убедитесь, что одна и та же привязка имеет тот же тип данных.
В следующем случае мы связываем два значения i и j. Вы можете видеть, что i не нужно быть первой привязкой.
let test = Foo.c(1, "c", 3)
switch test {
case let .b(i, j), let .c(j, _, i):
print("(\(i), \(j))")
default:
break
}
вывод
(3, 1)
Предостережение
Есть одно предостережение, которое вы должны знать о сложных случаях. Если вы используете предложение where для проверки дополнительных условий, убедитесь, что вы применяете его ко всем случаям в составных случаях.
let test = Foo.a(1)
switch test {
case .a(let i) where i == 5,
.b(_, let i) where i == 5:
print("Five(\(i))")
default:
print("Not five")
}
вывод
Not five
В приведенном выше примере будет напечатано «Не пять», поскольку 1 не равно 5 (очевидно).
Если вы примените где только в конце случая, это будет применяться только к последнему шаблону.
let test = Foo.a(1)
switch test {
case .a(let i),
.b(_, let i) where i == 5: // 1
print("Five(\(i))")
default:
print("Not five")
}
<1> Пункт Where будет применяться только к .b.
Выход:
Five(1)
В приведенном выше примере будет напечатано «Пять (1)», поскольку предложение where применяется только к .b.
Составной регистр не ограничивается перечислением, так как это функция переключателя регистра. В следующем примере используется составной случай с координатой, представленной кортежем.
let coordinate = (9, 0)
switch coordinate {
case (let distance, 0), (0, let distance):
print("On an axis, \(distance) from the origin")
default:
print("Not on an axis")
}
2. If case let / Guard case let
Если вас интересует только конкретный случай перечисления, вы можете использовать форму if let и guard let.
let test = Foo.a(1)
if case .a = test {
// Action for .a case
}
guard case .a = test else {
return
}
Вы можете использовать привязку значения так же, как и в случае с переключателем.
if case .a(let i) = test {
// Action for .a case
}
if case let .a(i) = test {
// Action for .a case
}
3. Дополнительно
Если связанное значение является необязательным, вы также можете сопоставить необязательное значение с образцом.
Это легче понять в действии. Прежде чем мы это сделаем, позвольте мне определить для этого новое перечисление. Он похож на Foo, но на этот раз все связанные значения являются необязательными.
enum Bar {
case a(Int?)
case b(Int?, Int?)
case c(Int?, String?, Int?)
case d(String?)
}
Если вы хотите извлечь первое значение из .a, вы можете сделать это следующим образом.
let test: Bar = Bar.a(1)
switch test {
case .a(let i):
print(i)
default:
break
}
Приведенный выше случай будет соответствовать .a независимо от его значения. Итак, и Bar.a(1), и Bar.a(nil) будут совпадать.
.some (Т)
Если вы хотите сопоставить case .a только тогда, когда связанное значение не равно нулю, вы можете сделать это следующим образом.
let test: Bar = Bar.a(1)
switch test {
case .a(let .some(i)):
print(i)
default:
break
}
Поскольку необязательно это просто еще одно перечисление. Здесь мы используем case .some для сопоставления с ненулевым значением.
enum Optional<Wrapped> {
case none
case some(Wrapped)
}
Вы также можете использовать ? как сокращение для соответствия .some. Итак, case .a(let .some(i)): становится case .a(let i?):.
let test: Bar = Bar.a(1)
switch test {
case .a(let i?):
print(i)
default:
break
}
.none
Как вы, возможно, уже знаете, мы можем сопоставить значение nil с .none.
Следующий переключатель будет соответствовать регистру с нулевым значением.
let test: Bar = Bar.a(nil)
switch test {
case .a(.none):
print("nil value")
default:
break
}
Смешанный и согласованный
Вы можете использовать это в случае с несколькими связанными значениями, чтобы получить желаемое условие.
let test: Bar = Bar.c(1, "Cat", nil)
switch test {
case .c(let i, .some, .none):
print(i)
default:
break
}
Приведенный выше случай переключения будет соответствовать случаю .c и извлекать первое значение независимо от его значения, но только если оно имеет ненулевое второе строковое значение и третье значение nil.
Вывод
Я всегда забываю об этих трех функциях (есть еще, но я не могу вспомнить о них прямо сейчас), поэтому я резюмирую их здесь. Если вы найдете эту статью полезной, пожалуйста, поделитесь ею с друзьями и подпишитесь на меня, чтобы узнать больше.