Swift enum inheritance
Можете ли вы наследовать перечисление в Swift? Каковы правила, о которых следует знать в отношении наследования enum?
Следующий тестовый код:
enum TemperatureUnit: Int {
case Kelvin, Celcius, Farenheit
}
enum TemperatureSubunit : Temperature {
}
генерирует
error: type 'TemperatureSubunit' does not conform to protocol 'RawRepresentable'
Ответы
Ответ 1
В языке Swift у нас есть структуры, перечисления и классы. Struct и Enum передаются путем копирования, но классы передаются по ссылке. Только классы поддерживают наследование, а Enum и Struct - нет.
Поэтому, чтобы ответить на ваш вопрос, у вас не может быть наследования с Enum (и типами Struct). Посмотрите здесь:
fooobar.com/questions/21779/...
Ответ 2
Как уже сказал Корпел, в настоящее время нет фактического наследования для Enums. Таким образом, невозможно, чтобы определенный Enum расширялся и наследовал случаи другого перечисления.
Однако я бы добавил к завершению, что Enums поддерживает протоколы и вместе с расширениями протокола, представленными в Swift 2, и новым подходом к протокольному программированию (см. это видео), можно реализовать что-то похожее на наследование. Это метод, который я использую для определения UITableViewController
: s, управляемого enums, для указания разделов таблицы и строк в каждом разделе и для добавления полезного поведения. См. Например, следующий пример кода:
import UIKit
protocol TableSection {
static var rows: [Self] { get }
var title: String { get }
var mandatoryField: Bool { get }
}
extension TableSection {
var mandatoryTitle: String {
if mandatoryField {
return "\(title)*"
} else {
return title
}
}
}
enum RegisterTableSection: Int, TableSection {
case Username
case Birthdate
case Password
case RepeatPassword
static var rows: [RegisterTableSection] {
return [.Username, .Password, .RepeatPassword]
}
var title: String {
switch self {
case .Username:
return "Username"
case .Birthdate:
return "Date of birth"
case .Password:
return "Password"
case .RepeatPassword:
return "Repeat password"
}
}
var mandatoryField: Bool {
switch self {
case .Username:
return true
case .Birthdate:
return false
case .Password:
return true
case .RepeatPassword:
return true
}
}
}
class ViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return RegisterTableSection.rows.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
guard let row = RegisterTableSection(rawValue: indexPath.row) else {
// This should never happen
return UITableViewCell()
}
let cell = UITableViewCell()
cell.textLabel?.text = row.mandatoryTitle
return cell
}
}
Предыдущий код отобразит следующую таблицу:
![Enum-defined table]()
Обратите внимание, что, реализуя протокол, наш RegisterTableSection
enum должен обеспечить реализацию методов и переменных, определенных в протоколе. И самое интересное, он наследует реализацию по умолчанию переменной mandatoryTitle
через расширение протокола TableSection
Я загрузил исходный код этого примера здесь
Ответ 3
Посмотрите на мой пример, это намного проще: Может ли перечисление содержать другие значения перечисления в Swift?
Подробнее
Проверено на:
- Xcode 9.2, Swift 4 и 3
- Xcode 10.2 (10E125) и 11.0 (11A420a), Swift 5
Решение
enum State {
case started
case succeeded
case failed
}
enum ActionState {
case state(value: State)
case cancelled
}
Результат
Перечисление ActionState имеет 4 значения:
.state(value: .started)
.state(value: .succeeded)
.state(value: .failed)
.cancelled
Еще один образец
import Foundation
enum StringCharactersTransformType {
case upperCase
case lowerCase
}
enum StringTransformType {
case state(value: StringCharactersTransformType)
case normal
static var upperCase: StringTransformType {
return .state(value: .upperCase)
}
static var lowerCase: StringTransformType {
return .state(value: .lowerCase)
}
}
var type = StringTransformType.normal
print(type)
type = .upperCase
print(type)
type = .lowerCase
print(type)
Результат
![enter image description here]()
Ответ 4
Попробуйте, обратите внимание, что ExtendedEnum здесь не наследуется от BaseEnum, вместо этого его rawValue определяется как регистр BaseEnum:
enum BaseEnum {
case none
case baseCase1
case baseCase2
func extendedType -> ExtendedEnum {
switch self {
case .none:
assert(false); return .baseCase1 // soft fail
case .baseCase1:
return .baseCase1 // ExtendedEnum case
case .baseCase2:
return .baseCase2
}
}
}
enum ExtendedEnum: BaseEnum {
case baseCase1 = .baseCase1
case baseCase2 = .baseCase2
case extendedCase1 = .none
case extendedCase2 = .none
}
Если вам нужно преобразовать ExtendedEnum в baseEnum, используйте extendedEnumInstance.rawValue. Вы также можете добавить вспомогательные методы в BaseEnum для преобразования в различные виды ExtendedEnum и, как правило, для упрощения процесса работы с различными типами, которые на самом деле не наследуются.
Этот метод позволяет вам получить все преимущества перечислений на разных уровнях наследования, если вы правильно обновляете и поддерживаете все перечисления. Вы можете продолжать "связывать" их вместе, чтобы создать дерево наследования перечислений, если хотите.
Вы также должны выдать ошибку в ваших операторах switch, когда ".none" является неожиданным; это говорит о том, что расширенный регистр перечислений был преобразован в базовый регистр перечислений, который не существует. Если вы планируете на самом деле использовать дело .none для другой цели, вам следует назвать это дело как-то иначе, например, ".notACase"