¿Cómo hacer que una enumeración se ajuste a un protocolo en Swift?

93

La documentación de Swift dice que las clases , estructuras y enumeraciones pueden ajustarse a los protocolos, y puedo llegar a un punto en el que todas se ajustan. Pero no puedo hacer que la enumeración se comporte como los ejemplos de clase y estructura :

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

No he descubierto cómo hacer simpleDescriptionque cambie como resultado de la llamada adjust(). Obviamente, mi ejemplo no hará eso porque el captador tiene un valor codificado, pero ¿cómo puedo establecer un valor para el simpleDescriptionmientras todavía se ajusta a ExampleProtocol?

Adrian Harris Crowne
fuente

Respuestas:

155

Este es mi intento:

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
fuente
Esto satisface el protocolo pero todavía tiene sentido como enumeración. ¡Buen trabajo!
David James
1
¡Increíble! Tenía la idea de crear un estado ajustado, pero no se me ocurrió que podría cambiar a .Ajustado en el método de ajuste. ¡Gracias!
Adrian Harris Crowne
Excelente puntero. Estaba un poco atascado en este. Sin embargo, una pregunta: ¿alguna razón por la que agregó el valor de retorno de Void a la función de ajuste?
jpittman
@jpittman porque la adjustfunción regresa Voiden ExampleProtocol, es lo mismo que usar mutating func adjust(). Si desea adjusttener un tipo de retorno, puede cambiar el protocolo a: gist.github.com/anjerodesu/e1bf640576a3b6fa415f
Angelo
1
No se pudo editar la respuesta para corregir el error de sintaxis, falta un punto, debería sercase .Base:
John Doe
44

Aquí está mi opinión.

Como esto es un enumy no un class, tienes que pensar diferente (TM) : es tu descripción la que tiene que cambiar cuando el "estado" de tus enumcambios (como lo señala @ 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

Espero que ayude.

Zedenem
fuente
Estoy de acuerdo con su opinión sobre la enumeración en sí y con el código que proporcionó. bonito.
4
Esta respuesta es más agradable y concisa que la aceptada.
Ricardo Sanchez-Saez
2
Solo una nota al margen de que puede eliminar SimpleEnumeration.Adjusted y reemplazarlo con solo ".Adjusted". Si el nombre de la enumeración cambia alguna vez, es una cosa menos que refactorizar.
Shaolo
Sí, esto es mejor. Gracias.
Arjun Kalidas
Sin embargo, esto no se ajusta al protocolo dado
barry
11

Aquí hay otro enfoque, utilizando solo los conocimientos adquiridos durante el recorrido hasta ese 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

Si desea que adjust()actúe como un conmutador (aunque no hay nada que sugiera que este sea el caso), use:

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

* (Aunque no menciona explícitamente cómo especificar un tipo de retorno y un protocolo)

Jack James
fuente
2
Creo que este enfoque es probablemente el mejor del grupo. La actualización rápida es que simpleDescription debería devolver self.rawValue
Justin Levi Winter
7

Aquí hay una solución que no cambia el valor de enumeración actual, sino sus valores de instancia (en caso de que sea útil para alguien).

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
fuente
Puntos extra para quien encuentre la manera de evitar todos esos cambios. Algo self = copy(self, self.desc + ", asdfasdf")
parecido a
4

No es posible definir variables sin getter y setter en enums y por lo tanto es imposible tener una variable que pueda modificar.

Puede ajustarse al protocolo pero no puede tener el mismo comportamiento con mutaciones que en las clases.

Tomáš Linhart
fuente
2

Es un enlace sobre enum en swift.

Las estructuras y enumeraciones son tipos de valor. De forma predeterminada, las propiedades de un tipo de valor no se pueden modificar desde sus métodos de instancia.enlace

Entonces, tienes que usar la función de mutación.

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
fuente
1

Otra opción es ajustar () para cambiar entre mayúsculas y minúsculas de la siguiente manera:

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
fuente
1

Aquí se basa en la respuesta 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
fuente
1

Se me ocurrió esto

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
fuente
0

aquí está mi código

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
LEVA
fuente
0

Mi primera contribución aquí:

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

¡Gracias por los demás!

Indra Rusmita
fuente
1
¿Podría agregar también una explicación?
Robert
@Robert debería explicarse por sí mismo como otros, pero la diferencia es que estoy usando el método init en enum y tengo una enumeración básica predeterminada. por lo que verá eso cuando cree un objeto enum como en la estructura y el ejemplo de clase en Swift playground.
Indra Rusmita
0

Este experimento también me desconcertó, debido a los ejemplos anteriores de SimpleClass y SimpleStructure que muestran que la propiedad simpleDescription se modifica internamente, lo que me hizo pensar que tenía que hacer lo mismo. Después de revisar las otras respuestas publicadas aquí y leer la documentación oficial de Apple Swift 2.1, se me ocurrió esto:

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

También observe que en los ejemplos dados por Apple para SimpleClass y SimpleStructure antes de este experimento, la descripción simple se pierde internamente - no puede recuperar el valor original (a menos que, por supuesto, lo guarde fuera de la clase / estructura); esto es lo que me impulsó a crear un método restore () para el ejemplo SimpleEnum, que le permite alternar entre valores. ¡Espero que sea útil para alguien!

William L. Marr III
fuente
0

Estaba pensando que el objetivo es simplemente retener el estado y usar una descripción para facilitar la lectura del estado actual:

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
fuente
0

Otra variación: usar valores asociados para mantener y mostrar la opción anterior (del formulario "Seleccionado 1, ajustado de 2, ajustado de 1, ajustado de 2, ajustado 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
fuente
-1

Qué tal esto

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
fuente