Как заставить перечисление соответствовать протоколу в Swift?


97

В документации Swift говорится, что классы , структуры и перечисления могут соответствовать протоколам, и я могу добраться до точки, где все они соответствуют. Но я не могу заставить перечисление вести себя так же, как примеры классов и структур :

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

Я не придумал, как заставить simpleDescriptionизмениться в результате звонка adjust(). В моем примере этого явно не будет, потому что у геттера жестко запрограммировано значение, но как я могу установить значение, simpleDescriptionпока оно все еще соответствует ExampleProtocol?

Ответы:


157

Это моя попытка:

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

Это удовлетворяет протоколу, но все же имеет смысл в качестве перечисления. Хорошая работа!
Дэвид Джеймс

1
Потрясающие! У меня была идея создать скорректированное состояние, но мне не приходило в голову, что я могу изменить его на .Adjusted в методе настройки. Благодарность!
Адриан Харрис Краун

Отличный указатель. Был немного зациклен на этом. Один вопрос: по какой причине вы добавили возвращаемое значение Void в функцию настройки?
jpittman 07

@jpittman, потому что adjustфункция возвращается Voidв ExampleProtocol, это то же самое, что просто использовать mutating func adjust(). Если вы хотите adjustиметь возвращаемый тип, вы можете изменить протокол на: gist.github.com/anjerodesu/e1bf640576a3b6fa415f
Анджело

1
Не удалось отредактировать ответ, чтобы исправить синтаксическую ошибку, в нем отсутствует точка, должно бытьcase .Base:
Джон Доу

45

Вот мой взгляд на это.

Поскольку это, enumа не a class, вы должны думать иначе (TM) : это ваше описание, которое должно измениться, когда «состояние» ваших enumизменений (как указано @ 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

Надеюсь, это поможет.


Я согласен с вашим мнением о самом перечислении и с предоставленным вами кодом. отлично.

4
Этот ответ лучше и лаконичнее принятого.
Рикардо Санчес-Саез

2
Замечу, что вы можете удалить SimpleEnumeration.Adjusted и заменить его просто ".Adjusted". Если имя перечисления когда-либо изменится, то на рефакторинг будет на одну вещь меньше.
Шаоло

Ага, так лучше. Спасибо.
Арджун Калидас

Однако это не соответствует данному протоколу
Барри

11

Вот еще один подход, использующий только знания, полученные в ходе тура до этого момента *

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

Если вы хотите, чтобы он adjust()действовал как переключатель (хотя нет никаких оснований предполагать, что это так), используйте:

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

* (Хотя здесь явно не упоминается, как указать тип возвращаемого значения и протокол)


2
Я думаю, что этот подход, вероятно, лучший из всех. Быстрое обновление состоит в том, что simpleDescription должен возвращать self.rawValue
Джастин Леви Винтер

7

Вот решение, которое не изменяет текущее значение перечисления, а вместо этого значения их экземпляров (на всякий случай, это полезно для всех).

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

Дополнительные баллы для тех, кто найдет способ избежать всех этих переключателей. Что-то self = copy(self, self.desc + ", asdfasdf")
вроде

4

Невозможно определить переменные без геттера и сеттера в перечислениях, и поэтому невозможно иметь переменную, которую вы можете изменить.

Вы можете соответствовать протоколу, но у вас не может быть такого же поведения с изменением, как в классах.


2

Это ссылка на enum in swift.

Структуры и перечисления - это типы значений. По умолчанию свойства типа значения не могут быть изменены из методов его экземпляра. ссылка на сайт

Затем вам нужно использовать функцию мутации.

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

Другой вариант - для adjust () переключаться между случаями следующим образом:

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

Основываясь на ответе Джека:

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

Я придумал это

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

вот мой код

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

Мой первый вклад здесь:

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

Спасибо другим!


1
Не могли бы вы также добавить пояснение?
Роберт

@Robert это должно быть объяснено само собой, как и другие, но отличается тем, что я использую метод init в enum и имею базовое перечисление по умолчанию. поэтому вы увидите это, когда создадите объект перечисления, как в примере структуры и класса в Swift Playground.
Индра Русмита,

0

Этот эксперимент тоже сбил меня с толку, поскольку в предыдущих примерах SimpleClass и SimpleStructure было показано, что свойство simpleDescription изменено внутри, что заставило меня подумать, что мне нужно сделать то же самое. После просмотра других ответов, размещенных здесь, и чтения официальной документации Apple Swift 2.1, я пришел к следующему:

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

Также обратите внимание, что в примерах, данных Apple для SimpleClass и SimpleStructure до этого эксперимента, простое описание потеряно внутри - вы не можете вернуть исходное значение (если, конечно, вы не сохраните его вне класса / структуры); именно это побудило меня создать метод restore () для примера SimpleEnum, который позволяет переключать его между значениями. Надеюсь, это кому-то будет полезно!


0

Я думал, что цель - просто сохранить состояние и использовать описание, чтобы его было легче читать:

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

Другой вариант: использование связанных значений для удержания и отображения предыдущего параметра (вида «Выбрано 1, настроено из 2, настроено из 1, настроено из 2, настроено из 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

как насчет этого

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"
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.