Подтвердить что ты не робот

Swift enum inheritance

Можете ли вы наследовать перечисление в Swift? Каковы правила, о которых следует знать в отношении наследования enum?

Следующий тестовый код:

enum TemperatureUnit: Int {
    case Kelvin, Celcius, Farenheit
}

enum TemperatureSubunit : Temperature {  
}

генерирует

error: type 'TemperatureSubunit' does not conform to protocol 'RawRepresentable'
4b9b3361

Ответ 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 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"