web-dev-qa-db-fra.com

Ajout d'un cas à une énumération existante avec un protocole

Je veux créer un protocol qui applique un certain cas sur tout enums conforme à ce protocol.

Par exemple, si j'ai un enum comme ceci:

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

Je veux l'étendre avec un protocol qui ajoute un autre cas:

case Fuzz(Int)

Est-ce possible?

19
cfischer

Conception

La solution consiste à utiliser un struct avec static variables.

Remarque: c'est ce qui se fait dans Swift pour Notification.Name

Voici une implémentation sur Swift

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
    }

}

Protocole

protocol CoolCar {

}

extension CoolCar {

    static var Yellow : Car {

        return Car(rawValue: "Yellow")
    }
}

extension Car : CoolCar {

}

Invoquer

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")
}

Remarque:

Veuillez noter que cette approche ne remplace pas enum, utilisez-la uniquement lorsque les valeurs ne sont pas connues au moment de la compilation.

35
user1046037

non, car vous ne pouvez pas déclarer un case en dehors d'un enum.

8
R Menke

Un extension peut ajouter un enum imbriqué, comme ceci:

enum Plants {
  enum Fruit {
     case banana
  }
} 


extension Plants {
  enum Vegetables {
     case potato
  }
}
6
Mark

Voici quelques prises supplémentaires qui peuvent aider quelqu'un:

En utilisant votre exemple:

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

Vous pouvez envisager de "l'imbriquer" dans un case de votre choix enum:

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

Avec cette solution, il devient plus laborieux d'accéder au type associé aux cas "cachés". Mais cette simplification pourrait en fait être bénéfique dans certaines applications.

Une autre alternative passe juste par recréer et étendre tout en ayant un moyen de convertir Foo en _ enumFooExtended étendu (par exemple avec un init personnalisé):

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)
        }
    }
}

Il peut y avoir de nombreux endroits où l'une, l'autre ou les deux solutions n'ont absolument aucun sens, mais je suis sûr qu'elles peuvent être utiles à quelqu'un là-bas (même si ce n'est que comme un exercice).

2
fbeeper