Swift Language
Перечисления
Поиск…
замечания
Подобно структурам и в отличие от классов, перечисления являются типами значений и копируются вместо ссылки, когда они передаются.
Дополнительные сведения о перечислениях см. В разделе «Быстрый язык программирования» .
Основные перечисления
Перечисление предоставляет набор связанных значений:
enum Direction {
case up
case down
case left
case right
}
enum Direction { case up, down, left, right }
Значения Enum могут использоваться их полностью квалифицированным именем, но вы можете опустить имя типа, когда это можно сделать:
let dir = Direction.up
let dir: Direction = Direction.up
let dir: Direction = .up
// func move(dir: Direction)...
move(Direction.up)
move(.up)
obj.dir = Direction.up
obj.dir = .up
Самый фундаментальный способ сравнения / извлечения значений enum - с помощью оператора switch
:
switch dir {
case .up:
// handle the up case
case .down:
// handle the down case
case .left:
// handle the left case
case .right:
// handle the right case
}
Простые перечисления автоматически Hashable
, Equatable
и имеют преобразования строк:
if dir == .down { ... }
let dirs: Set<Direction> = [.right, .left]
print(Direction.up) // prints "up"
debugPrint(Direction.up) // prints "Direction.up"
Перечисления со связанными значениями
События Enum могут содержать одну или несколько полезных нагрузок ( связанных значений ):
enum Action {
case jump
case kick
case move(distance: Float) // The "move" case has an associated distance
}
Полезная нагрузка должна быть предоставлена при создании экземпляра значения перечисления:
performAction(.jump)
performAction(.kick)
performAction(.move(distance: 3.3))
performAction(.move(distance: 0.5))
Оператор switch
может извлечь связанное значение:
switch action {
case .jump:
...
case .kick:
...
case .move(let distance): // or case let .move(distance):
print("Moving: \(distance)")
}
Выделение одного случая может быть выполнено с использованием if case
:
if case .move(let distance) = action {
print("Moving: \(distance)")
}
Синтаксис guard case
можно использовать для последующего использования:
guard case .move(let distance) = action else {
print("Action is not move")
return
}
Перечисления со связанными значениями по умолчанию не Equatable
. Реализация оператора ==
должна выполняться вручную:
extension Action: Equatable { }
func ==(lhs: Action, rhs: Action) -> Bool {
switch lhs {
case .jump: if case .jump = rhs { return true }
case .kick: if case .kick = rhs { return true }
case .move(let lhsDistance): if case .move (let rhsDistance) = rhs { return lhsDistance == rhsDistance }
}
return false
}
Косвенная полезная нагрузка
Обычно перечисления не могут быть рекурсивными (потому что они потребуют бесконечного хранения):
enum Tree<T> {
case leaf(T)
case branch(Tree<T>, Tree<T>) // error: recursive enum 'Tree<T>' is not marked 'indirect'
}
indirect
ключевое слово заставляет enum хранить свою полезную нагрузку со слоем косвенности, а не хранить его в строке. Вы можете использовать это ключевое слово в одном случае:
enum Tree<T> {
case leaf(T)
indirect case branch(Tree<T>, Tree<T>)
}
let tree = Tree.branch(.leaf(1), .branch(.leaf(2), .leaf(3)))
indirect
также работает на всей переписке, делая при необходимости косвенным косвенным образом:
indirect enum Tree<T> {
case leaf(T)
case branch(Tree<T>, Tree<T>)
}
Значения Raw и Hash
Перечисления без полезных нагрузок могут иметь необработанные значения любого литерального типа:
enum Rotation: Int {
case up = 0
case left = 90
case upsideDown = 180
case right = 270
}
Перечисления без определенного типа не выставляют свойство rawValue
enum Rotation {
case up
case right
case down
case left
}
let foo = Rotation.up
foo.rawValue //error
Предполагается, что значения целых сырых значений начинаются с 0 и монотонно возрастают:
enum MetasyntacticVariable: Int {
case foo // rawValue is automatically 0
case bar // rawValue is automatically 1
case baz = 7
case quux // rawValue is automatically 8
}
Строковые исходные значения могут быть синтезированы автоматически:
enum MarsMoon: String {
case phobos // rawValue is automatically "phobos"
case deimos // rawValue is automatically "deimos"
}
Необработанное перечисление автоматически соответствует RawRepresentable . Вы можете получить соответствующее исходное значение значения .rawValue
с помощью .rawValue
:
func rotate(rotation: Rotation) {
let degrees = rotation.rawValue
...
}
Вы также можете создать перечисление из необработанного значения с помощью init?(rawValue:)
:
let rotation = Rotation(rawValue: 0) // returns Rotation.Up
let otherRotation = Rotation(rawValue: 45) // returns nil (there is no Rotation with rawValue 45)
if let moon = MarsMoon(rawValue: str) {
print("Mars has a moon named \(str)")
} else {
print("Mars doesn't have a moon named \(str)")
}
Если вы хотите получить хеш-значение определенного перечисления, вы можете получить доступ к его hashValue, хеш-значение вернет индекс перечисления, начиная с нуля.
let quux = MetasyntacticVariable(rawValue: 8)// rawValue is 8
quux?.hashValue //hashValue is 3
Инициализаторы
В Enums могут быть настраиваемые методы init, которые могут быть более полезными, чем init?(rawValue:)
по умолчанию init?(rawValue:)
. Enums также может хранить значения. Это может быть полезно для хранения значений, которые они инициализировали и извлекали это значение позже.
enum CompassDirection {
case north(Int)
case south(Int)
case east(Int)
case west(Int)
init?(degrees: Int) {
switch degrees {
case 0...45:
self = .north(degrees)
case 46...135:
self = .east(degrees)
case 136...225:
self = .south(degrees)
case 226...315:
self = .west(degrees)
case 316...360:
self = .north(degrees)
default:
return nil
}
}
var value: Int = {
switch self {
case north(let degrees):
return degrees
case south(let degrees):
return degrees
case east(let degrees):
return degrees
case west(let degrees):
return degrees
}
}
}
Используя этот инициализатор, мы можем сделать это:
var direction = CompassDirection(degrees: 0) // Returns CompassDirection.north
direction = CompassDirection(degrees: 90) // Returns CompassDirection.east
print(direction.value) //prints 90
direction = CompassDirection(degrees: 500) // Returns nil
Перечисления разделяют многие функции с классами и структурами
Перечисления в Swift намного мощнее, чем некоторые из их коллег на других языках, таких как C. Они имеют множество функций с классами и структурами , такими как определение инициализаторов , вычисляемых свойств , методов экземпляров , соответствия протоколов и расширений .
protocol ChangesDirection {
mutating func changeDirection()
}
enum Direction {
// enumeration cases
case up, down, left, right
// initialise the enum instance with a case
// that's in the opposite direction to another
init(oppositeTo otherDirection: Direction) {
self = otherDirection.opposite
}
// computed property that returns the opposite direction
var opposite: Direction {
switch self {
case .up:
return .down
case .down:
return .up
case .left:
return .right
case .right:
return .left
}
}
}
// extension to Direction that adds conformance to the ChangesDirection protocol
extension Direction: ChangesDirection {
mutating func changeDirection() {
self = .left
}
}
var dir = Direction(oppositeTo: .down) // Direction.up
dir.changeDirection() // Direction.left
let opposite = dir.opposite // Direction.right
Вложенные перечисления
Вы можете встраивать перечисления один внутри другого, это позволяет вам структурировать иерархические перечисления, чтобы быть более организованными и понятными.
enum Orchestra {
enum Strings {
case violin
case viola
case cello
case doubleBasse
}
enum Keyboards {
case piano
case celesta
case harp
}
enum Woodwinds {
case flute
case oboe
case clarinet
case bassoon
case contrabassoon
}
}
И вы можете использовать его так:
let instrment1 = Orchestra.Strings.viola
let instrment2 = Orchestra.Keyboards.piano