如何在Swift中使枚举符合协议?

104

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

我还没有想出如何在调用adjust()后更改simpleDescription的方法。我的示例显然无法做到这一点,因为getter具有硬编码的值,但是我如何在仍符合ExampleProtocol的情况下设置simpleDescription的值呢?

15个回答

170

这是我的尝试:

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。谢谢! - Adrian Harris Crowne
优秀的指针。这个有点卡住了我。不过有一个问题:你为什么要将Void的返回值添加到adjust函数中呢? - jpittman
1
@jpittman 因为 ExampleProtocol 中的 adjust 函数返回 Void,所以与使用 mutating func adjust() 相同。如果您想让 adjust 具有返回类型,可以将协议更改为:https://gist.github.com/anjerodesu/e1bf640576a3b6fa415f - Angelo
1
无法编辑答案以更正语法错误,它缺少一个点,应该是 case .Base: - John Doe
有人能帮我点个赞吗?另外,#POP 是一个敏感话题。Visa 曾经在 #POP 面试中对我进行了严格的测试。 - CDM social medias in bio

47

这是我的看法。

由于这是一个enum而不是一个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

希望这能有所帮助。


我同意你对枚举本身的看法,以及你提供的代码。不错。 - user3095716
5
这个答案比被采纳的那一个更友善、更简明。 - Ricardo Sanchez-Saez
2
只是一点小建议,您可以将SimpleEnumeration.Adjusted删除并替换为".Adjusted"。如果枚举的名称发生更改,则需要重构的内容就会减少一个。 - Shaolo
好的,这样更好。谢谢。 - Arjun Kalidas
这与给定的协议不符。 - barry

12

这里是另一种方法,仅利用此时为止从旅游中获得的知识*

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。 - Justin Levi Winter

9

这里有一个解决方案,它不会改变当前枚举值,而是改变它们的实例值(以防对任何人有用)。

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") 的方式。 - DiogoNeves

4

在枚举类型中,没有 getter 和 setter 是不能定义变量的,因此无法有一个可以修改的变量。

你可以遵循协议,但是不能像类一样具有可变行为。


2

这是关于Swift中枚举类型的链接

结构体和枚举类型都是值类型。默认情况下,值类型的属性不能在其实例方法内被修改。链接

如果要修改值类型的属性,则必须使用mutating函数。

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
这是对杰克回答的进一步建议:
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

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
另一种变体:使用关联值来保存和显示先前的选项(形式为“选定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"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接