Как заставить перечисление соответствовать протоколу в 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.