Перечисление с ассоциированным значением, соответствующим CaseIterable, RawRepresentable

#swift #enums

#swift #перечисления

Вопрос:

Я пытаюсь сделать перечисление со связанным значением, соответствующим регистру, RawRepresentable. Меня устраивают некоторые значения по умолчанию для связанных значений при инициализации с помощью rawValue.

 enum GenresAssociated: CaseIterable, RawRepresentable, Equatable {
    case unknown(String)
    case blues(String)
    case classical(String)
    // Implementing CaseIterable
    typealias AllCases = [GenresAssociated]
    // Enums can have no storage, but the class they are in CAN. Note 'static' in declaration
    static var allCases: [GenresAssociated] = [.unknown(""), .blues(""), .classical("")]


    typealias RawValue = Int
    var rawValue: Int {

        // MARK: This causes a crash for unknown reason
        return GenresAssociated.allCases.firstIndex(where: {  if case self = $0 { return true } else { return false } } ) ?? 0
    }
    init?(rawValue: Int) {
        guard GenresAssociated.allCases.indices.contains(rawValue) else { return nil }
        self = GenresAssociated.allCases[rawValue]
    }
}
  

Есть ли какой-либо способ сделать это без переключения вручную по всем случаям, т. е. без такого кода:

     typealias RawValue = Int
    var rawValue: Int {
        switch self {
        case .unknown:
            return 0
        case .blues:
            return 1
        case .classical:
            return 2
        }
    }
  

Примечательно то, что очень похожий код работает просто отлично, например

 enum EnumWithValue {
    case one(NSString!), two(NSString!), three(NSString!)
}

let arrayOfEnumsWithValues: [EnumWithValue] = [.one(nil), .two(nil), .three("Hey")]

if let index = arrayOfEnumsWithValues.firstIndex(where: { if case .two = $0 { return true }; return false }) {
    print(".two found at index (index)") //prints ".two found at index 1"
}
  

Комментарии:

1. Я бы не стал использовать RawRepresentable для перечисления со связанными значениями, поскольку RawRepresentable протокол указывает, что вы можете «переключаться между пользовательским типом и связанным типом rawValue без потери значения исходного RawRepresentable типа» ( developer.apple.com/documentation/swift/rawrepresentable ). Это означало бы, что ваши связанные значения также каким-то образом содержатся в rawValue .

2. Полностью согласен, @Palle. Структура данных является разновидностью kext (а также стресс-тестом для Enum). Да, я играю с перечислениями здесь, пытаясь освоить их.

Ответ №1:

Наконец-то я получил его для работы Mirror !

 var rawValue: Int {
    let selfCaseName = Mirror(reflecting: self).children.first!.label!

    return GenresAssociated.allCases.firstIndex(where: { (genre) in
        let switchingCaseName = Mirror(reflecting: genre).children.first!.label!

        return switchingCaseName == selfCaseName
    })!
}
  

Не обращайте внимания на принудительное разворачивание, здесь это безопасно.

Комментарии:

1. Определенно рабочее решение, @J. Doe. Я также иногда использую Mirror в качестве последнего средства. Но я также пытаюсь выяснить, что не так с моим синтаксисом, т. Е. создаю ли я цикл памяти или сталкиваюсь с проблемой из-за лени или сбоев, или что-то в этом роде.

2. Не могли бы вы добавить еще несколько подробностей о том, как / почему это работает. Это потенциально может стать очень популярным (с большим количеством голосов) и полезным ответом. Спасибо

Ответ №2:

Этот параметр будет работать с перечислениями, содержащими как случаи rawValue, так и случаи с associatedValues:

 var caseName: String { 
   let result = String(describing: self)
   let assocValName = Mirror(reflecting: self).children.first?.label ?? "failed"

   return !result.contains("(") ? result : assocValName
}