Добавление случая к существующему перечислению с протоколом

Я хочу создать protocol, который накладывает определенный случай на все enums, соответствующие этому protocol.

Например, если у меня есть enum, как это:

enum Foo{
    case bar(baz: String)
    case baz(bar: String)
}

Я хочу расширить его с помощью protocol, который добавит еще один случай:

case Fuzz(Int)

Возможно ли это?

Ответ 1

Дизайн

Обходной задачей является использование переменных struct с static.

Примечание. Это делается в Swift 3 для Notification.Name

Ниже приведена реализация в Swift 3

Struct:

struct Car : RawRepresentable, Equatable, Hashable, Comparable {

    typealias RawValue = String

    var rawValue: String

    static let Red  = Car(rawValue: "Red")
    static let Blue = Car(rawValue: "Blue")

    //MARK: Hashable

    var hashValue: Int {
        return rawValue.hashValue
    }

    //MARK: Comparable

    public static func <(lhs: Car, rhs: Car) -> Bool {

        return lhs.rawValue < rhs.rawValue
    }

}

Протокол

protocol CoolCar {

}

extension CoolCar {

    static var Yellow : Car {

        return Car(rawValue: "Yellow")
    }
}

extension Car : CoolCar {

}

Вызов

let c1 = Car.Red


switch c1 {
case Car.Red:
    print("Car is red")
case Car.Blue:
    print("Car is blue")
case Car.Yellow:
    print("Car is yellow")
default:
    print("Car is some other color")
}

if c1 == Car.Red {
    print("Equal")
}

if Car.Red > Car.Blue {
    print("Red is greater than Blue")
}

Примечание:

Обратите внимание, что этот подход не является заменой enum, используйте это, только когда значения не известны во время компиляции.

Ответ 2

нет, так как вы не можете объявить case вне enum.

Ответ 3

An extension может добавить вложенный enum, например:

enum Plants {
  enum Fruit {
     case banana
  }
} 


extension Plants {
  enum Vegetables {
     case potato
  }
}

Ответ 4

Вот пара дополнительных выплат, которые могут помочь кому-то:

Используя ваш пример:

enum Foo {
    case bar(baz: String)
    case baz(bar: String)
} 

Вы можете считать, что он "вложен" в case вашего собственного enum:

enum FooExtended {
    case foo(Foo) // <-- Here will live your instances of `Foo`
    case fuzz(Int)
}

С помощью этого решения становится более трудоемким доступ к "скрытым" случаям, связанным с типом. Но это упрощение может быть полезным в некоторых приложениях.

Другая альтернатива проходит путем просто воссоздания и расширения ее, имея способ конвертировать Foo в расширенный enum FooExtended (например, с пользовательским init):

enum FooExtended {
    case bar(baz: String)
    case baz(bar: String)
    case fuzz(Int)

    init(withFoo foo: Foo) {
        switch foo {
        case .bar(let baz):
            self =  .bar(baz: baz)
        case .baz(let bar):
            self = .baz(bar: bar)
        }
    }
}

Там может быть много мест, где один, другой или оба этих решения не имеют абсолютно никакого смысла, но я уверен, что они могут быть полезны кому-то там (даже если это упражнение).