Jak sprawić, by wyliczenie było zgodne z protokołem w Swift?


97

Dokumentacja Swift mówi, że wszystkie klasy , struktury i wyliczenia mogą być zgodne z protokołami i mogę dojść do punktu, w którym wszystkie są zgodne. Ale nie mogę sprawić, by wyliczenie zachowywało się tak, jak przykłady klas i struktur :

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

Nie wymyśliłem, jak sprawić, simpleDescriptionby zmienił się w wyniku rozmowy adjust(). Mój przykład oczywiście tego nie zrobi, ponieważ metoda pobierająca ma wartość zakodowaną na stałe, ale jak mogę ustawić wartość dla, simpleDescriptionjednocześnie zachowując zgodność z ExampleProtocol?

Odpowiedzi:


157

To jest moja próba:

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

Jest to zgodne z protokołem, ale nadal ma sens jako wyliczenie. Dobra robota!
David James

1
Niesamowite! Miałem pomysł na stworzenie stanu dostosowanego, ale nie przyszło mi do głowy, że mógłbym zmienić go na .Regulacja w metodzie dopasowania. Dzięki!
Adrian Harris Crowne

Doskonały wskaźnik. Trochę utknąłem na tym. Jedno pytanie: czy jest jakiś powód, dla którego dodałeś wartość zwracaną przez Void do funkcji Adjust?
jpittman,

@jpittman, ponieważ adjustfunkcja zwraca Voidw ExampleProtocol, to to samo, co samo użycie mutating func adjust(). Jeśli chcesz adjustmieć zwracany typ, możesz zmienić protokół na: gist.github.com/anjerodesu/e1bf640576a3b6fa415f
Angelo

1
Nie można edytować odpowiedzi, aby poprawić błąd składni, brakuje kropki, powinno byćcase .Base:
John Doe

45

Oto moje spojrzenie na to.

Ponieważ jest to enuma nie a class, musisz myśleć inaczej (TM) : to twój opis musi się zmienić, gdy „stan” twoich enumzmian (jak wskazał @ 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

Mam nadzieję, że to pomoże.


zgadzam się z twoim podejściem do samego wyliczenia i z podanym przez ciebie kodem. ładny.

4
Ta odpowiedź jest ładniejsza i bardziej zwięzła niż ta przyjęta.
Ricardo Sanchez-Saez

2
Na marginesie uwaga, że ​​możesz usunąć SimpleEnumeration.Adjusted i zastąpić tylko „.Adjusted”. Jeśli nazwa wyliczenia kiedykolwiek się zmieni, to refaktoryzacja jest o jedną rzecz mniej.
Shaolo

Tak, tak jest lepiej. Dzięki.
Arjun Kalidas

To jednak nie jest zgodne z podanym protokołem
barry

11

Oto inne podejście, wykorzystujące tylko wiedzę zdobytą podczas wycieczki do tego momentu *

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

Jeśli chcesz, aby adjust()działał jako przełącznik (chociaż nic nie sugeruje, że tak jest), użyj:

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

* (Chociaż nie wspomniano wyraźnie, jak określić typ zwrotu i protokół)


2
Myślę, że to podejście jest prawdopodobnie najlepsze ze wszystkich. Szybka aktualizacja polega na tym, że simpleDescription powinien zwrócić self.rawValue
Justin Levi Winter

7

Oto rozwiązanie, które nie zmienia bieżącej wartości wyliczenia, ale zamiast tego wartości ich wystąpienia (na wypadek, gdyby było przydatne dla każdego).

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

Dodatkowe punkty dla każdego, kto znajdzie sposób na uniknięcie tych wszystkich zmian. Coś w stylu tej fikcyjnej kopiiself = copy(self, self.desc + ", asdfasdf")
DiogoNeves

4

Nie jest możliwe zdefiniowanie zmiennych bez metody pobierającej i ustawiającej w wyliczeniach, dlatego nie można mieć zmiennej, którą można modyfikować.

Możesz dostosować się do protokołu, ale nie możesz mieć takiego samego zachowania z mutacją jak w klasach.


2

To jest łącze o enum in swift.

Struktury i wyliczenia są typami wartości. Domyślnie właściwości typu wartości nie mogą być modyfikowane z poziomu jego metod instancji. połączyć

Następnie musisz użyć funkcji mutacji.

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

Inną opcją jest funkcja Adjust (), aby przełączać się między przypadkami w następujący sposób:

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

Oto odpowiedź Jacka:

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

Wymyśliłem to

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

oto mój kod

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

Mój pierwszy wkład tutaj:

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

Dzięki za innych!


1
Czy mógłbyś również dodać wyjaśnienie?
Robert

@Robert powinno być wyjaśnione samodzielnie, jak inne, ale różne są to, że używam metody init w wyliczeniu i mam domyślne podstawowe wyliczenie. więc zobaczysz to podczas tworzenia obiektu wyliczenia, takiego jak struktura i przykład klasy w Swift zabaw.
Indra Rusmita

0

Ten eksperyment również mnie odrzucił, ponieważ poprzednie przykłady SimpleClass i SimpleStructure pokazały wewnętrzną modyfikację właściwości simpleDescription, co spowodowało, że pomyślałem, że muszę zrobić to samo. Po przejrzeniu innych odpowiedzi zamieszczonych tutaj i przeczytaniu oficjalnej dokumentacji Apple Swift 2.1 wymyśliłem to:

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

Zauważ również, że w przykładach podanych przez Apple dla SimpleClass i SimpleStructure przed tym eksperymentem, prosty opis jest tracony wewnętrznie - nie możesz odzyskać pierwotnej wartości (chyba że zapiszesz ją poza klasą / strukturą); to właśnie skłoniło mnie do utworzenia metody restore () dla przykładu SimpleEnum, która pozwala na przełączanie się między wartościami. Mam nadzieję, że to komuś się przyda!


0

Myślałem, że celem jest po prostu zachowanie stanu i użycie opisu, aby uczynić bieżący stan łatwiejszym do odczytania:

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

Inny wariant: użycie powiązanych wartości do zatrzymania i wyświetlenia poprzedniej opcji (w postaci „Wybrano 1, skorygowano z 2, skorygowano od 1, skorygowano z 2, skorygowano od 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

co powiesz na to

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"
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.