3 менее известных способа использования перечислений Swift

Перечисление (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.

Вывод

Я всегда забываю об этих трех функциях (есть еще, но я не могу вспомнить о них прямо сейчас), поэтому я резюмирую их здесь. Если вы найдете эту статью полезной, пожалуйста, поделитесь ею с друзьями и подпишитесь на меня, чтобы узнать больше.