Como fazer um enum estar em conformidade com um protocolo em Swift?


93

A documentação do Swift diz que classes , structs e enums podem estar em conformidade com os protocolos e posso chegar a um ponto em que todos eles estão em conformidade. Mas não consigo fazer com que o enum se comporte exatamente como os exemplos de classe e estrutura :

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

Não descobri como fazer simpleDescriptionpara mudar como resultado de uma ligação adjust(). Meu exemplo obviamente não fará isso porque o getter tem um valor embutido no código, mas como posso definir um valor para o simpleDescriptionenquanto ainda estou em conformidade com o ExampleProtocol?

Respostas:


155

Esta é minha tentativa:

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

Isso satisfaz o protocolo, mas ainda faz sentido como enum. Bom trabalho!
— David James

1
Impressionante! Tive a ideia de criar um estado ajustado, mas não me ocorreu que poderia mudar para .Adjustado no método de ajuste. Obrigado!
— Adrian Harris Crowne

Excelente ponteiro. Fiquei um pouco preso neste. Porém, uma pergunta: algum motivo pelo qual você adicionou o valor de retorno de Void à função de ajuste?
— jpittman de

@jpittman porque a adjustfunção retorna Voidno ExampleProtocol, é o mesmo que usar mutating func adjust(). Se desejar adjustter um tipo de retorno, você pode alterar o protocolo para: gist.github.com/anjerodesu/e1bf640576a3b6fa415f
— Angelo

1
Não foi possível editar a resposta para corrigir o erro de sintaxe, está faltando um ponto, deveria sercase .Base:
— John Doe

44

Aqui está minha opinião sobre isso.

Como se trata de um enume não de um class, você deve pensar diferente (TM) : é a sua descrição que deve mudar quando o "estado" das suas enummudanças (como apontado por @ 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 ajude.


concordo com sua opinião sobre a enum em si e com o código que você forneceu. legais.

4
Esta resposta é mais agradável e sucinta do que a aceita.
— Ricardo Sanchez-Saez

2
Apenas uma nota lateral que você pode remover o SimpleEnumeration.Adjusted e substituir por apenas ".Adjusted". Se o nome da enumeração mudar, é uma coisa a menos para refatorar.
— Shaolo

Sim, está melhor. Obrigado.
— Arjun Kalidas

No entanto, isso não está de acordo com o protocolo fornecido
— barry

11

Aqui está outra abordagem, usando apenas o conhecimento adquirido com o passeio até aquele ponto *

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 você deseja que adjust()atue como um alternador (embora não haja nada que sugira que este seja o caso), use:

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

* (Embora não mencione explicitamente como especificar um tipo de retorno e um protocolo)


2
Acho que essa abordagem é provavelmente a melhor do grupo. A atualização rápida é que o simpleDescription deve retornar self.rawValue
— Justin Levi Winter

7

Aqui está uma solução que não altera o valor enum atual, mas seus valores de instância (apenas no caso de ser útil para alguém).

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

Pontos extras para quem encontrar uma maneira de evitar todas essas mudanças. Algo nos moldes desta cópia fictíciaself = copy(self, self.desc + ", asdfasdf")
— DiogoNeves

4

Não é possível definir variáveis ​​sem getter e setter em enums e, portanto, é impossível ter uma variável que você possa modificar.

Você pode estar em conformidade com o protocolo, mas não pode ter o mesmo comportamento com a mutação como nas classes.


2

É um link sobre enum in swift.

Estruturas e enumerações são tipos de valor. Por padrão, as propriedades de um tipo de valor não podem ser modificadas a partir de seus métodos de instância. ligação

Então, você tem que usar a função 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

Outra opção é ajustar () para alternar entre os casos da seguinte forma:

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

Aqui está a construção da resposta 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
        }
    }
}

1

Eu vim com isso

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

aqui está meu 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

0

Minha primeira contribuição aqui:

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

Obrigado pelos outros!


1
Você também poderia adicionar uma explicação?
— Robert

@Robert deve ser autoexplicado como os outros, mas a diferença é que estou usando o método init no enum e tenho o enum básico padrão. então você verá isso quando criar um objeto enum como na estrutura e exemplo de classe no playground do Swift.
— Indra Rusmita

0

Esse experimento também me confundiu, devido aos exemplos anteriores de SimpleClass e SimpleStructure mostrando a propriedade simpleDescription sendo modificada internamente, o que me fez pensar que precisava fazer a mesma coisa. Depois de examinar as outras respostas postadas aqui e ler a documentação oficial do Apple Swift 2.1, eu descobri o seguinte:

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

Observe também que nos exemplos fornecidos pela Apple para SimpleClass e SimpleStructure antes deste experimento, a descrição simples é perdida internamente - você não pode obter o valor original de volta (a menos, é claro, que o salve fora da classe / estrutura); foi isso que me levou a criar um método restore () para o exemplo SimpleEnum, que permite alternar entre os valores. Espero que isso seja útil para alguém!


0

Eu estava pensando que o objetivo é simplesmente manter o estado e usar uma descrição para tornar o estado atual mais fácil de ler:

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

Outra variação: Usar valores associados para manter e exibir a opção anterior (da forma "Selecionado 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"

-1

que tal agora

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"
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.