Come rendere un enum conforme a un protocollo in Swift?


93

La documentazione Swift dice che le classi , le strutture e le enumerazioni possono essere tutte conformi ai protocolli e posso arrivare a un punto in cui sono tutte conformi. Ma non riesco a fare in modo che l' enum si comporti come gli esempi di classe e struttura :

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

Non ho capito come ottenere la simpleDescriptionmodifica a seguito della chiamata adjust(). Il mio esempio ovviamente non lo farà perché il getter ha un valore hard-coded, ma come posso impostare un valore per il simpleDescriptiontempo ancora conforme a ExampleProtocol?

Risposte:


155

Questo è il mio tentativo:

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

Ciò soddisfa il protocollo ma ha ancora senso come enum. Buon lavoro!
David James il

1
Eccezionale! Avevo l'idea di creare uno stato adattato, ma non mi è venuto in mente di poter passare a .Adjusted nel metodo di regolazione. Grazie!
Adrian Harris Crowne

Ottimo puntatore. Era un po 'bloccato su questo. Una domanda però: c'è qualche motivo per cui hai aggiunto il valore di ritorno di Void alla funzione di regolazione?
jpittman

@jpittman perché la adjustfunzione ritorna Voidin ExampleProtocol, è come usare semplicemente mutating func adjust(). Se vuoi adjustavere un tipo di ritorno, puoi cambiare il protocollo in: gist.github.com/anjerodesu/e1bf640576a3b6fa415f
Angelo

1
Impossibile modificare la risposta per correggere l'errore di sintassi, manca un punto, dovrebbe esserecase .Base:
John Doe

44

Ecco la mia opinione.

Poiché questo è an enume non a class, devi pensare in modo diverso (TM) : è la tua descrizione che deve cambiare quando lo "stato" dei tuoi enumcambiamenti (come sottolineato da @ 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

Spero che aiuti.


Sono d'accordo con la tua opinione sull'enumerazione stessa e con il codice che hai fornito. simpatico.

4
Questa risposta è più carina e più succinta di quella accettata.
Ricardo Sanchez-Saez

2
Solo una nota a margine che puoi rimuovere SimpleEnumeration.Adjusted e sostituirlo con solo ".Adjusted". Se il nome dell'enumerazione cambia mai, è una cosa in meno da refactoring.
Shaolo

Sì, questo è meglio. Grazie.
Arjun Kalidas

Questo però non è conforme al protocollo dato
Barry

11

Ecco un altro approccio, utilizzando solo le conoscenze acquisite dal tour fino a quel momento *

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

Se vuoi adjust()agire come un interruttore (anche se non c'è nulla che suggerisca che sia così), usa:

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

* (Anche se non menziona esplicitamente come specificare un tipo di ritorno e un protocollo)


2
Penso che questo approccio sia probabilmente il migliore del gruppo. L'aggiornamento rapido è che la descrizione semplice dovrebbe restituire self.rawValue
Justin Levi Winter

7

Ecco una soluzione che non modifica il valore enum corrente, ma invece i valori di istanza (nel caso in cui sia utile a chiunque).

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

Punti extra per chi trova un modo per evitare tutti quegli scambi. Qualcosa sulla self = copy(self, self.desc + ", asdfasdf")
falsariga

4

Non è possibile definire variabili senza getter e setter in enumerazioni e quindi è impossibile avere una variabile che si può modificare.

Puoi conformarti al protocollo ma non puoi avere lo stesso comportamento con la mutazione come nelle classi.


2

È un collegamento sull'enumerazione in swift.

Le strutture e le enumerazioni sono tipi di valore. Per impostazione predefinita, le proprietà di un tipo di valore non possono essere modificate dall'interno dei suoi metodi di istanza. collegamento

Quindi, devi usare la funzione mutante.

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

1

Un'altra opzione è che Adjust () passi da un caso all'altro come segue:

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

1

Basandosi sulla risposta di 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
        }
    }
}

1

Ho pensato a questo

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

0

ecco il mio codice

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

0

Il mio primo contributo qui:

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

Grazie per gli altri!


1
Potresti aggiungere anche una spiegazione?
Robert

@ Robert dovrebbe essere auto spiegato come gli altri, ma le differenze sono che sto usando il metodo init in enum e ho l'enum di base predefinito. quindi lo vedrai quando crei un oggetto enum come nella struttura e nell'esempio di classe in Swift playground.
Indra Rusmita,

0

Questo esperimento ha spiazzato anche me, a causa dei precedenti esempi SimpleClass e SimpleStructure che mostravano la proprietà simpleDescription modificata internamente, il che mi ha fatto pensare che dovevo fare la stessa cosa. Dopo aver esaminato le altre risposte pubblicate qui e letto la documentazione ufficiale di Apple Swift 2.1, mi è venuto in mente questo:

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

Si noti inoltre che negli esempi forniti da Apple per SimpleClass e SimpleStructure prima di questo esperimento, la semplice descrizione viene persa internamente - non è possibile recuperare il valore originale (a meno che, ovviamente, non lo si salvi al di fuori della classe / struttura); questo è ciò che mi ha spinto a creare un metodo restore () per l'esempio SimpleEnum, che ti consente di alternarlo avanti e indietro tra i valori. Spero che questo sia utile a qualcuno!


0

Stavo pensando che l'obiettivo è semplicemente quello di mantenere lo stato e utilizzare una descrizione per rendere lo stato corrente più facile da leggere:

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

0

Un'altra variante: utilizzo dei valori associati per mantenere e visualizzare l'opzione precedente (della forma "Selezionato 1, regolato da 2, regolato da 1, regolato da 2, regolato da 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"

-1

cosa ne pensi di questo

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"
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.