Comment rendre une énumération conforme à un protocole dans Swift?

93

La documentation de Swift dit que les classes , les structures et les énumérations peuvent toutes se conformer aux protocoles, et je peux arriver à un point où elles se conforment toutes. Mais je ne peux pas faire en sorte que l' énumération se comporte comme les exemples de classe et de structure :

protocol ExampleProtocol {
    var simpleDescription: String { get set }
    mutating func adjust()
}

class SimpleClass: ExampleProtocol {
    var simpleDescription: String = "A very simple class."
    var anotherProperty: Int = 69105

    func adjust() {
        simpleDescription += " Now 100% adjusted."
    }
}

var a = SimpleClass()
a.adjust()
let aDescription = a.simpleDescription

struct SimpleStructure: ExampleProtocol {
    var simpleDescription: String = "A simple structure"

    mutating func adjust() {
        simpleDescription += " (adjusted)"
    }
}

var b = SimpleStructure()
b.adjust()
let bDescription = b.simpleDescription

enum SimpleEnum: ExampleProtocol {
    case Base

    var simpleDescription: String {
        get {
            return "A Simple Enum"
        }
        set {
            newValue
        }
    }

    mutating func adjust() {
        self.simpleDescription += ", adjusted"
    }
}

var c = SimpleEnum.Base
c.adjust()
let cDescription = c.simpleDescription

Je n'ai pas compris comment faire simpleDescriptionchanger le à la suite d'un appel adjust(). Mon exemple ne le fera évidemment pas car le getter a une valeur codée en dur, mais comment puis-je définir une valeur pour le simpleDescriptiontout en restant conforme au ExampleProtocol?

Adrian Harris Crowne
la source

Réponses:

155

Voici ma tentative:

protocol ExampleProtocol {
    var simpleDescription: String { get }
    mutating func adjust()
}

enum ExampleEnum : ExampleProtocol {
    case Base, Adjusted

    var simpleDescription: String {
        return self.getDescription()
    }

    func getDescription() -> String {
        switch self {
        case .Base:
            return "A simple description of enum"
        case .Adjusted:
            return "Adjusted description of enum"
        }
    }

    mutating func adjust() {
        self = ExampleEnum.Adjusted
    }
}

var c = ExampleEnum.Base
c.adjust()
let cDescription = c.simpleDescription
Hu Qiang
la source
Cela satisfait le protocole mais a toujours du sens en tant qu'énumération. Bon travail!
David James
1
Impressionnant! J'avais cette idée de créer un état ajusté, mais il ne m'est pas venu à l'esprit que je pourrais changer en .Ajusté dans la méthode d'ajustement. Merci!
Adrian Harris Crowne le
Excellent pointeur. Était un peu coincé sur celui-ci. Une question cependant: une raison pour laquelle vous avez ajouté la valeur de retour de Void à la fonction d'ajustement?
jpittman
@jpittman car la adjustfonction retourne Voiddans le ExampleProtocol, c'est la même chose que d'utiliser mutating func adjust(). Si vous souhaitez adjustavoir un type de retour, vous pouvez changer le protocole en: gist.github.com/anjerodesu/e1bf640576a3b6fa415f
Angelo
1
Impossible de modifier la réponse pour corriger l'erreur de syntaxe, il manque un point, devrait êtrecase .Base:
John Doe
44

Voici mon point de vue.

Comme il s'agit d'un enumet non d'un class, vous devez penser différemment (TM) : c'est votre description qui doit changer lorsque "l'état" de vos enumchangements (comme indiqué par @ hu-qiang).

enum SimpleEnumeration: ExampleProtocol {
  case Basic, Adjusted

  var description: String {
    switch self {
    case .Basic:
      return "A simple Enumeration"
    case .Adjusted:
      return "A simple Enumeration [adjusted]"
    }
  }

  mutating func adjust()  {
    self = .Adjusted
  }
}

var c = SimpleEnumeration.Basic
c.description
c.adjust()
c.description

J'espère que cela pourra aider.

Zedenem
la source
Je suis d'accord avec votre point de vue sur l'énumération elle-même et avec le code que vous avez fourni. agréable.
4
Cette réponse est plus agréable et plus succincte que celle acceptée.
Ricardo Sanchez-Saez
2
Juste une note latérale que vous pouvez supprimer le SimpleEnumeration.Adjusted et le remplacer par juste ".Adjusted". Si le nom de l'énumération change un jour, c'est une chose de moins à refactoriser.
Shaolo
Ouais, c'est mieux. Merci.
Arjun Kalidas
Cela n'est pas conforme au protocole donné
barry
11

Voici une autre approche, en utilisant uniquement les connaissances acquises de la visite jusqu'à ce point *

enum SimpleEnumeration: String, ExampleProtocol {
    case Basic = "A simple enumeration", Adjusted = "A simple enumeration (adjusted)"

    var simpleDescription: String {
        get {
            return self.toRaw()
        }
    }

    mutating func adjust() {
        self = .Adjusted
    }
}

var c = SimpleEnumeration.Basic
c.adjust()
let cDescription = c.simpleDescription

Si vous voulez adjust()agir comme une bascule (bien que rien ne suggère que ce soit le cas), utilisez:

mutating func adjust() {
    switch self {
    case .Basic:
        self = .Adjusted
    default:
        self = .Basic
    }
}

* (Bien qu'il ne mentionne pas explicitement comment spécifier un type de retour et un protocole)

Jack James
la source
2
Je pense que cette approche est probablement la meilleure du groupe. La mise à jour rapide est que la description simple devrait renvoyer self.rawValue
Justin Levi Winter
7

Voici une solution qui ne change pas la valeur d'énumération actuelle, mais leurs valeurs d'instance à la place (juste au cas où cela serait utile à quiconque).

enum ProtoEnumeration : ExampleProtocol {
    case One(String)
    case Two(String)

    var simpleDescription: String {
        get {
            switch self {
            case let .One(desc):
                return desc
            case let .Two(desc):
                return desc
            }
        }
    }
    mutating func adjust() {
        switch self {
        case let .One(desc):
            self = .One(desc + ", adjusted 1")
        case let .Two(desc):
            self = .Two(desc + ", adjusted 2")
        }
    }
}

var p = ProtoEnumeration.One("test")
p.simpleDescription
p.adjust()
p.simpleDescription
DiogoNeves
la source
Des points supplémentaires pour quiconque trouve un moyen d'éviter tous ces changements. Quelque chose dans le sens de cette copie fictiveself = copy(self, self.desc + ", asdfasdf")
DiogoNeves
4

Il n'est pas possible de définir des variables sans getter et setter dans les énumérations et il est donc impossible d'avoir une variable que vous pouvez modifier.

Vous pouvez vous conformer au protocole mais vous ne pouvez pas avoir le même comportement avec la mutation que dans les classes.

Tomáš Linhart
la source
2

C'est un lien sur enum en swift.

Les structures et les énumérations sont des types valeur. Par défaut, les propriétés d'un type valeur ne peuvent pas être modifiées à partir de ses méthodes d'instance. lien

Ensuite, vous devez utiliser la fonction de mutation.

enum ProtocolEnum: ExampleProtocol {
    case on, off
    var simpleDescription: String {
        switch self {
        case .on:
            return "Switch is ON"
        case .off:
            return "Switch is OFF"
        }
    }
    mutating func adjust() {
        switch self {
        case .on:
            self = off
        case .off:
            self = on
        }
    }
}

var c = ProtocolEnum.on
c.simpleDescription
c.adjust()
let cDescription = c.simpleDescription
Jeff Gu Kang
la source
1

Une autre option consiste à ajuster () pour basculer entre les cas comme suit:

enum SimpleEnum: ExampleProtocol {
    case Foo, Bar

    var simpleDescription: String {
    get {
        let value = self == .Foo
            ? "Foo"
            : "Bar"
        return "A simple \(value) enum."
    }
    }

    mutating func adjust() {
        self = self == .Foo
            ? .Bar
            : .Foo
    }
}
Endersstocker
la source
1

Voici en s'appuyant sur la réponse de Jack:

protocol ICanWalk {
    var description: String { get }
    mutating func stepIt()
}

enum TwoStepsForwardThreeStepsBack: Int, ICanWalk {
    case Base = 0, Step1, Step2

    var description: String {
        return "Step \(self.rawValue)"
    }

    mutating func stepIt() {
        if let nextStep = TwoStepsForwardThreeStepsBack( rawValue: self.rawValue + 1 ) {
            // going forward.
            self = nextStep
        } else {
            // back to the base.
            self = TwoStepsForwardThreeStepsBack.Base
        }
    }
}
Alex Akhtyrskiy
la source
1

Je suis venu avec ça

protocol ExampleProtocol {
    var simpleDescription: String { get }
    mutating func adjust()
}

enum Seat: ExampleProtocol {
    case WindowSeat, MiddleSeat, AisleSeat

    var simpleDescription : String {
        switch self {
        case .WindowSeat:
            return "Window Seat"
        case .MiddleSeat:
            return "Middle Seat"
        case .AisleSeat:
            return "Aisle Seat"
        }
    }

    mutating func adjust() {
        switch self {
        case .WindowSeat:
            self = .MiddleSeat
        case .MiddleSeat:
            self = . AisleSeat
        case .AisleSeat:
            self = .WindowSeat
        }
    }
}

var seat = Seat.MiddleSeat
print(seat.simpleDescription) // Middle Seat
seat.adjust()
print(seat.simpleDescription) // Aisle Seat
Groot
la source
0

voici mon code

enum SimpleEnum: ExampleProtocol {
    case Base, Adjusted
    var simpleDescription: String {
        get {
            var description = "A simple enum."
            switch self {
            case .Base:
                return description
            case .Adjusted:
                return description + " - [adjusted]"
            }
        }
    }
    mutating func adjust() {
        self = SimpleEnum.Adjusted
    }
}
var simpleEnum = SimpleEnum.Base
simpleEnum.adjust()
simpleEnum.simpleDescription
CAME
la source
0

Ma première contribution ici:

enum SimpleEnum: ExampleProtocol {
    case Basic(String), Adjusted(String)
    init() {
        self = SimpleEnum.Basic("A simple Enum")

    }

    var simpleDescription: String {
        get {
            switch self {
            case let .Basic(string):
                return string
            case let .Adjusted(string):
                return string
            }
        }
    }

    mutating func adjust() {
        self = SimpleEnum.Adjusted("full adjusted")

    }
}

var c = SimpleEnum()
c.adjust()
let cDescription = c.simpleDescription

Merci pour les autres!

Indra Rusmita
la source
1
Pourriez-vous également ajouter une explication?
Robert
@Robert, cela devrait être auto-expliqué comme les autres, mais la différence est que j'utilise la méthode init dans enum et que j'ai une énumération de base par défaut. vous verrez donc cela lorsque vous créez un objet enum comme dans l'exemple de structure et de classe dans Swift Playground.
Indra Rusmita
0

Cette expérience m'a également déconcerté, en raison des exemples précédents de SimpleClass et SimpleStructure montrant la propriété simpleDescription modifiée en interne, ce qui m'a fait penser que je devais faire la même chose. Après avoir examiné les autres réponses publiées ici et lu la documentation officielle d'Apple Swift 2.1, j'ai trouvé ceci:

protocol ExampleProtocol {
     var simpleDescription: String { get }
     mutating func adjust()
}

enum SimpleEnum: ExampleProtocol {
    case Simple
    case Adjusted

    var simpleDescription: String {
        switch self {
        case .Simple:
            return "A simple enumeration"
        case .Adjusted:
            return "A simple enumeration somewhat changed."
        }
    }

    mutating func adjust() {
        self = .Adjusted
    }

    mutating func restore() {
        self = .Simple
    }
}

var d: SimpleEnum = .Simple
d.simpleDescription

d.adjust()
d.simpleDescription

d.restore()
d.simpleDescription

Notez également que dans les exemples donnés par Apple pour SimpleClass et SimpleStructure avant cette expérience, la description simple est perdue en interne - vous ne pouvez pas récupérer la valeur d'origine (à moins bien sûr que vous ne la sauvegardiez en dehors de la classe / structure); c'est ce qui m'a incité à créer une méthode restore () pour l'exemple SimpleEnum, qui vous permet de basculer entre les valeurs. J'espère que cela est utile à quelqu'un!

William L. Marr III
la source
0

Je pensais que le but est simplement de conserver l'état et d'utiliser une description pour rendre l'état actuel plus facile à lire:

enum SimpleEnum: ExampleProtocol {

    case Default, Adjusted

    init() {
        self = .Default
    }

    var simpleDescription: String { get { return "\(self) Value" }}

    mutating func adjust() {
        self = .Adjusted
    }
}

var simpleEnum = SimpleEnum()
simpleEnum.adjust()
let adjustedSimple = simpleEnum.simpleDescript
Johan
la source
0

Autre variante: utilisation des valeurs associées pour maintenir et afficher l'option précédente (de la forme "Sélectionné 1, ajusté de 2, ajusté de 1, ajusté de 2, ajusté de 1")

protocol ExampleProtocol {
     var simpleDescription: String { get }
     mutating func adjust()
}

indirect enum EnumWithDescription: ExampleProtocol {
    case option1(EnumWithDescription?)
    case option2(EnumWithDescription?)
    var simpleDescription: String {
        return "Selected " + getDescription()
    }
    internal func getDescription() -> String {
        var currentValue: String
        let previousValue : EnumWithDescription?
        switch self {
        case .option1(let previous):
            currentValue = "1"
            previousValue = previous
        case .option2(let previous):
            currentValue = "2"
            previousValue = previous
        }
        if let adjustedFrom = previousValue?.getDescription() {
            return "\(currentValue) adjusted from \(adjustedFrom)"
        }
        else {
            return "\(currentValue)"
        }
    }
    mutating func adjust() {
        switch self {
        case .option1:
            self = .option2(self)
        case .option2:
            self = .option1(self)
        }
    }
}
var d = EnumWithDescription.option1(nil)
d.simpleDescription
d.adjust()
d.adjust()
d.simpleDescription
// Output: "Selected 1, adjusted from 2, adjusted from 1, adjusted from 2, adjusted from 1"
nkalvi
la source
-1

que dis-tu de ça

enum SimpleEnum : ExampleProtocol {
    case Desc(String)
    init() {
        self = Desc("a simple enum")
    }
    var simpleDescription:String {
        get {
            return (Mirror(reflecting: self).children.first!.value as? String)!
        }
    }
    mutating func adjust() {
        self = SimpleEnum.Desc(self.desc + " adjusted")
    }
}
var e = SimpleEnum()
e.simpleDescription    # => "a simple enum"
e.adjust()
e.simpleDescription    # => "a simple enum adjusted"
michex
la source