Методы перечислений
В меню рядом с каждой строкой напечатана цена. Чтобы её узнать, не нужно идти к кассе: достаточно посмотреть на саму строку меню. Ответ живёт там же, где и напиток.
В первом уроке цену считала отдельная функция price(of:), и ей передавали напиток. Код о напитках можно держать и внутри самого перечисления. Тогда вариант сам отвечает на вопрос о себе:
enum Drink {
case tea, coffee
func price(cups: Int) -> Int {
switch self {
case .tea:
return 2 * cups
case .coffee:
return 3 * cups
}
}
}
let drink = Drink.coffee
print(drink.price(cups: 2)) // 6Давайте разберём программу:
- Функция
price(cups:)объявлена внутри фигурных скобок перечисления. Такая функция — метод перечисления, какhasSuffixу строки. drink.price(cups: 2)вызывает метод через точку у значенияdrink.- Внутри метода
self— то значение, у которого метод вызвали. Здесь этоdrink, то есть кофе. switch selfвыбирает ветку.coffee, и метод возвращает3 * 2, то есть6.
Метод и self
Метод объявляют так же, как обычную функцию, только внутри перечисления, обычно после списка вариантов. Что важно знать о методе:
- его вызывают у значения через точку:
drink.price(cups: 2)илиDrink.tea.price(cups: 3); selfвнутри метода — само это значение, «эта строка меню»;switch self— обычное тело такого метода: у каждого варианта своя ветка, иdefaultне нужен.
Вычисляемое свойство
У строки есть count: его читают через точку и без скобок. То, что читают у значения через точку без скобок, называют свойством. Перечислению тоже можно дать свойство, только вычислять его будет ваш код.
Пусть теперь на чеке кофе записано число чашек. Цена заказа зависит от этого числа:
enum Order {
case tea
case coffee(cups: Int)
var price: Int {
switch self {
case .tea:
return 2
case .coffee(let cups):
return cups * 3
}
}
}
print(Order.coffee(cups: 2).price) // 6Вычисляемое свойство — свойство, значение которого нигде не хранится, а вычисляется заново при каждом чтении. Его объявление состоит из трёх частей:
var— вычисляемое свойство объявляют только черезvar: сletкод не скомпилируется;price: Int— имя свойства и его тип;- фигурные скобки — код, который возвращает значение через
return.
Читают свойство без скобок: .price, а не .price(). В ветке .coffee(let cups) шаблон достаёт число чашек, и для двух чашек свойство даёт 6.
Короткую ветку можно записать и в одну строку: case .tea: return 2. Это та же ветка, только без переноса строки.
Когда писать метод, а когда свойство? Свойство подходит, когда ответу ничего не нужно, кроме самого значения: цена заказа, размер чашки. Метод нужен, когда ответу нужны данные извне, как число чашек в price(cups:).
Здесь картинка с меню неточна. Цену в меню печатают один раз, а вычисляемое свойство ничего не хранит: price выполняет switch при каждом чтении.
Частая ошибка
Метод вызвали у самого типа, а не у значения:
print(Drink.price(cups: 2)) // ✗error: instance member 'price' cannot be used on type 'Drink'; did you mean to use a value of this type instead?Программа не компилируется. Drink — всё меню, а не один напиток, и непонятно, чью цену считать: чая или кофе. Компилятор подсказывает, что нужно значение этого типа. Вызовите метод у варианта, Drink.tea.price(cups: 2), или у константы: drink.price(cups: 2).
Главное
- Метод перечисления объявляют внутри его фигурных скобок и вызывают у значения через точку:
drink.price(cups: 2). selfвнутри метода — значение, у которого его вызвали, аswitch selfвыбирает ветку по варианту.- Вычисляемое свойство ничего не хранит: его код выполняется при каждом чтении, а читают его без скобок.