Перечисление определяет общий тип для группы связанных значений и позволяет работать с этими значениями в пути типобезопасного в коде.
Если вы знакомы с C, вы будете знать, что перечисления C присваивают связанные имена ряду целочисленных значений. Перечисления в Swift гораздо более гибкие, и не обязательно указывать значение для каждого случая перечисления. Если значение (известное как необработанное значение) предоставляется для каждого случая перечисления, это может быть строка, символ или значение любого целочисленного типа или типа с плавающей точкой.
В качестве альтернативы, случаи перечисления могут указывать связанные значения любого типа, которые должны храниться вместе с каждым другим значением регистра, так же, как объединения или варианты делают в других языках. Вы можете определить общий набор связанных случаев как часть одного перечисления, каждый из которых имеет свой набор значений соответствующих типов, связанных с ним.
Перечисления в Swift являются первоклассными типами сами по себе. Они принимают множество функций, традиционно поддерживаемых только классами, таких как вычисляемые свойства для предоставления дополнительной информации о текущем значении перечисления и методы экземпляра для обеспечения функциональности, связанной со значениями, которые представляет перечисление. Перечисления также могут определять инициализаторы для предоставления начального значения регистра; может быть расширен, чтобы расширить их функциональность за пределы их первоначальной реализации; и может соответствовать протоколам для обеспечения стандартной функциональности.
Для получения дополнительной информации об этих возможностях см. Свойства , Методы , Инициализация , Расширения и Протоколы .
Синтаксис перечисления
Вы вводите перечисления с enum
ключевым словом и помещаете их полное определение в пару фигурных скобок:
enum SomeEnumeration {
// enumeration definition goes here
}
Вот пример для четырех основных пунктов компаса:
enum CompassPoint {
case north
case south
case east
case west
}
Значения , определенные в перечислении (таких , как north
, south
, east
, и west
) являются его перечисление случаев . Вы используете case
ключевое слово, чтобы ввести новые случаи перечисления.
ЗАМЕТКА
Случаи перечисления Swift не имеют целочисленного значения, установленного по умолчанию, в отличие от языков, таких как C и Objective-C. В
CompassPoint
приведенном выше примере,north
,south
,east
иwest
не неявно равны0
,1
,2
и3
. Вместо этого различные случаи перечисления являются значениями сами по себе, с явно определенным типомCompassPoint
.
В одной строке может отображаться несколько случаев, разделенных запятыми:
enum Planet {
case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}
Каждое определение перечисления определяет новый тип. Как и другие типы в Swift, их имена (такие как CompassPoint
и Planet
) начинаются с заглавной буквы. Дайте типы перечисления в единственном числе, а не во множественном числе, чтобы они читались как очевидные:
var directionToHead = CompassPoint.west
Тип directionToHead
выводится, когда он инициализируется одним из возможных значений CompassPoint
. После того, directionToHead
как объявлено как CompassPoint
, вы можете установить его на другое CompassPoint
значение, используя более короткий синтаксис точки:
directionToHead = .east
Тип directionToHead
уже известен, и поэтому вы можете сбросить тип при установке его значения. Это делает код более читабельным при работе с явно типизированными значениями перечисления.
Сопоставление значений перечисления с оператором переключения
Вы можете сопоставить отдельные значения перечисления с switch
оператором:
directionToHead = .south
switch directionToHead {
case .north:
print("Lots of planets have a north")
case .south:
print("Watch out for penguins")
case .east:
print("Where the sun rises")
case .west:
print("Where the skies are blue")
}
// Prints "Watch out for penguins"
Вы можете прочитать этот код как:
«Учитывайте ценность directionToHead
. В случае, если оно равно .north
, выведите . В случае, если это равняется , напечатайте . ”"Lots of planets have a north"
.south
"Watch out for penguins"
…и так далее.
Как описано в Потоке управления , switch
оператор должен быть исчерпывающим при рассмотрении случаев перечисления. Если case
for .west
опущен, этот код не компилируется, потому что он не учитывает полный список CompassPoint
случаев. Требование полноты гарантирует, что случаи перечисления не будут случайно пропущены.
Если case
для каждого случая перечисления нецелесообразно предоставлять , вы можете предоставить default
случай, чтобы охватить все случаи, которые явно не рассматриваются:
let somePlanet = Planet.earth
switch somePlanet {
case .earth:
print("Mostly harmless")
default:
print("Not a safe place for humans")
}
// Prints "Mostly harmless"
Перебирая случаи перечисления
Для некоторых перечислений полезно иметь коллекцию всех случаев этого перечисления. Вы можете включить это, написав после имени перечисления. Swift предоставляет коллекцию всех дел в качестве свойства типа перечисления. Вот пример:: CaseIterable
allCases
enum Beverage: CaseIterable {
case coffee, tea, juice
}
let numberOfChoices = Beverage.allCases.count
print("\(numberOfChoices) beverages available")
// Prints "3 beverages available"
В приведенном выше примере вы пишете Beverage.allCases
для доступа к коллекции, которая содержит все случаи Beverage
перечисления. Вы можете использовать allCases
как любую другую коллекцию - элементы коллекции являются экземплярами типа перечисления, поэтому в этом случае они являются Beverage
значениями. В приведенном выше примере подсчитывается, сколько существует случаев, а в приведенном ниже примере используется for
цикл для итерации по всем случаям.
for beverage in Beverage.allCases {
print(beverage)
}
// coffee
// tea
// juice
Синтаксис, использованный в приведенных выше примерах, помечает перечисление как соответствующее CaseIterable
протоколу. Для получения информации о протоколах см. Протоколы .
Связанные ценности
Примеры в предыдущем разделе показывают, как случаи перечисления являются определенным (и типизированным) значением сами по себе. Вы можете установить константу или переменную в Planet.earth
и проверить это значение позже. Однако иногда полезно иметь возможность хранить значения других типов вместе с этими значениями регистра. Эта дополнительная информация называется связанным значением , и она меняется каждый раз, когда вы используете этот случай в качестве значения в своем коде.
Вы можете определить перечисления Swift для хранения связанных значений любого данного типа, и типы значений могут быть разными для каждого случая перечисления, если это необходимо. Перечисления, подобные этим, известны как различимые объединения , теговые объединения или варианты в других языках программирования.
Например, предположим, что системе отслеживания запасов необходимо отслеживать продукты по двум различным типам штрих-кода. Некоторые продукты помечены 1D штрих - коды в формате UPC, который использует номера 0
для 9
. Каждый штрих-код имеет цифру системы счисления, за которой следуют пять цифр кода производителя и пять цифр кода продукта. За ними следует контрольная цифра, чтобы убедиться, что код был отсканирован правильно:
Другие продукты помечены двумерными штрих-кодами в формате QR-кода, которые могут использовать любой символ ISO 8859-1 и могут кодировать строку длиной до 2953 символов:
В системе отслеживания запасов удобно хранить штрих-коды UPC в виде кортежа из четырех целых чисел, а штрих-коды QR-кода в виде строки любой длины.
В Swift перечисление для определения штрих-кодов продуктов любого типа может выглядеть следующим образом:
enum Barcode {
case upc(Int, Int, Int, Int)
case qrCode(String)
}
Это можно прочитать как:
«Определить тип перечисления под названием Barcode
, которое может принимать либо значение upc
с соответствующим значением типа ( Int
, Int
, Int
, Int
), или значение qrCode
с соответствующим значением типа String
.»
Это определение не предоставляет никаких фактических значений Int
или String
значений - оно просто определяет тип связанных значений, которые Barcode
константы и переменные могут хранить, когда они равны Barcode.upc
или Barcode.qrCode
.
Затем вы можете создавать новые штрих-коды, используя любой тип:
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
В этом примере создается новая переменная с именем productBarcode
и присваивается ей значение Barcode.upc
со связанным значением кортежа .(8, 85909, 51226, 3)
Вы можете назначить одному и тому же продукту другой тип штрих-кода:
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")
На этом этапе оригинал Barcode.upc
и его целочисленные значения заменяются новым Barcode.qrCode
и его строковым значением. Константы и переменные типа Barcode
могут хранить либо a, .upc
либо a .qrCode
(вместе со связанными с ними значениями), но они могут хранить только один из них в любой момент времени.
Вы можете проверить различные типы штрих-кодов с помощью оператора switch, аналогично примеру в разделе « Сопоставление значений перечисления с оператором Switch» . Однако на этот раз связанные значения извлекаются как часть оператора switch. Вы извлекаете каждое связанное значение как константу (с let
префиксом) или переменную (с var
префиксом) для использования в switch
теле кейса:
switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case .qrCode(let productCode):
print("QR code: \(productCode).")
}
// Prints "QR code: ABCDEFGHIJKLMNOP."
Если все связанные значения для случая перечисления извлекаются как константы, или если все извлекаются как переменные , для краткости можно поместить одиночную var
или let
аннотацию перед именем случая:
switch productBarcode {
case let .upc(numberSystem, manufacturer, product, check):
print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).")
case let .qrCode(productCode):
print("QR code: \(productCode).")
}
// Prints "QR code: ABCDEFGHIJKLMNOP."
Сырые ценности
Пример штрих-кода в связанных значениях показывает, как случаи перечисления могут объявить, что они хранят связанные значения различных типов. В качестве альтернативы связанным значениям случаи перечисления могут быть предварительно заполнены значениями по умолчанию (так называемыми необработанными значениями ), которые имеют одинаковый тип.
Вот пример, который хранит необработанные значения ASCII вместе с именованными случаями перечисления:
enum ASCIIControlCharacter: Character {
case tab = "\t"
case lineFeed = "\n"
case carriageReturn = "\r"
}
Здесь необработанные значения для вызываемого перечисления ASCIIControlCharacter
определены как имеющие тип Character
, и для них установлены некоторые из наиболее распространенных управляющих символов ASCII. Character
значения описаны в строках и символах .
Необработанные значения могут быть строками, символами или любыми типами целых чисел или чисел с плавающей точкой. Каждое необработанное значение должно быть уникальным в объявлении перечисления.
ЗАМЕТКА
Необработанные значения не совпадают с соответствующими значениями. Необработанные значения устанавливаются в предварительно заполненные значения, когда вы сначала определяете перечисление в своем коде, как три кода ASCII выше. Необработанное значение для конкретного случая перечисления всегда одинаково. Связанные значения устанавливаются при создании новой константы или переменной на основе одного из случаев перечисления и могут каждый раз меняться.
Неявно присвоенные необработанные значения
Когда вы работаете с перечислениями, которые хранят целочисленные или строковые необработанные значения, вам не нужно явно назначать необработанное значение для каждого случая. Если вы этого не сделаете, Swift автоматически назначит значения для вас.
Например, когда целые числа используются для необработанных значений, неявное значение для каждого случая на единицу больше, чем в предыдущем случае. Если в первом случае значение не установлено, его значение равно 0
.
Перечисление ниже является уточнением предыдущего Planet
перечисления с целочисленными необработанными значениями для представления порядка каждой планеты от Солнца:
enum Planet: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}
В приведенном выше примере Planet.mercury
имеет явное необработанное значение 1
, Planet.venus
имеет неявное необработанное значение 2
и т. Д.
Когда строки используются для необработанных значений, неявное значение для каждого случая - это текст имени этого случая.
Перечисление ниже является уточнением предыдущего CompassPoint
перечисления со строковыми необработанными значениями для представления имени каждого направления:
enum CompassPoint: String {
case north, south, east, west
}
В приведенном выше примере CompassPoint.south
имеет неявное необработанное значение "south"
и т. Д.
Вы получаете доступ к необработанному значению регистра перечисления с помощью его rawValue
свойства:
let earthsOrder = Planet.earth.rawValue
// earthsOrder is 3
let sunsetDirection = CompassPoint.west.rawValue
// sunsetDirection is "west"
Инициализация из необработанного значения
Если вы определяете перечисление с типом необработанного значения, перечисление автоматически получает инициализатор, который принимает значение типа необработанного значения (в качестве вызываемого параметра rawValue
) и возвращает либо регистр перечисления, либо nil
. Вы можете использовать этот инициализатор, чтобы попытаться создать новый экземпляр перечисления.
Этот пример идентифицирует Уран из его необработанного значения 7
:
let possiblePlanet = Planet(rawValue: 7)
// possiblePlanet is of type Planet? and equals Planet.uranus
Однако не все возможные Int
значения найдут подходящую планету. Из-за этого инициализатор необработанного значения всегда возвращает необязательныйрегистр перечисления. В приведенном выше примере possiblePlanet
имеет тип Planet?
или «необязательно Planet
».
ЗАМЕТКА
Инициализатор необработанного значения является неисправным инициализатором, поскольку не каждое необработанное значение будет возвращать регистр перечисления. Для получения дополнительной информации см. Failable Initializer .
Если вы попытаетесь найти планету с положением 11
, необязательное Planet
значение, возвращаемое инициализатором необработанного значения, будет nil
:
let positionToFind = 11
if let somePlanet = Planet(rawValue: positionToFind) {
switch somePlanet {
case .earth:
print("Mostly harmless")
default:
print("Not a safe place for humans")
}
} else {
print("There isn't a planet at position \(positionToFind)")
}
// Prints "There isn't a planet at position 11"
В этом примере используется необязательное связывание, чтобы попытаться получить доступ к планете с необработанным значением 11
. Оператор создает необязательный параметр и устанавливает значение этого необязательного параметра, если его можно извлечь. В этом случае невозможно получить планету с положением , и вместо этого выполняется ветвь.if let somePlanet = Planet(rawValue: 11)
Planet
somePlanet
Planet
11
else
Рекурсивные перечисления
Рекурсивное перечисление является перечислением , которое имеет еще один экземпляр перечисления в качестве связанного значения для одного или нескольких случаев перечисления. Вы указываете, что регистр перечисления является рекурсивным, записывая indirect
перед ним, что говорит компилятору вставить необходимый уровень косвенности.
Например, вот перечисление, которое хранит простые арифметические выражения:
enum ArithmeticExpression {
case number(Int)
indirect case addition(ArithmeticExpression, ArithmeticExpression)
indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}
Вы также можете написать indirect
перед началом перечисления, чтобы включить косвенное обращение для всех случаев перечисления, имеющих соответствующее значение:
indirect enum ArithmeticExpression {
case number(Int)
case addition(ArithmeticExpression, ArithmeticExpression)
case multiplication(ArithmeticExpression, ArithmeticExpression)
}
Это перечисление может хранить три вида арифметических выражений: простое число, сложение двух выражений и умножение двух выражений. В addition
и multiplication
случаи , связанные значения , которые являются также арифметические выражения-эти соответствующие значения позволяют гнездовых выражений. Например, выражение имеет номер в правой части умножения, а другое выражение - в левой части умножения. Поскольку данные являются вложенными, перечисление, используемое для хранения данных, также должно поддерживать вложенность - это означает, что перечисление должно быть рекурсивным. Код ниже показывает рекурсивное перечисление, создаваемое для :(5 + 4) * 2
ArithmeticExpression
(5 + 4) * 2
let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))
Рекурсивная функция - это простой способ работы с данными, имеющими рекурсивную структуру. Например, вот функция, которая оценивает арифметическое выражение:
func evaluate(_ expression: ArithmeticExpression) -> Int {
switch expression {
case let .number(value):
return value
case let .addition(left, right):
return evaluate(left) + evaluate(right)
case let .multiplication(left, right):
return evaluate(left) * evaluate(right)
}
}
print(evaluate(product))
// Prints "18"
Эта функция вычисляет простое число, просто возвращая соответствующее значение. Он оценивает сложение или умножение, оценивая выражение в левой части, вычисляя выражение в правой части, а затем добавляя или умножая их.
0 комментариев