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

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()的结果改变。我的示例显然不会这样做,因为 Getter有一个硬编码的值,但是我怎样才能在仍然符合 ExampleProtocol的情况下为 simpleDescription设置一个值呢?

60088 次浏览

It is not possible to define variables without getter and setter in enums and therefore it is impossible to have a variable that you can modify.

You can conform to the protocol but you cannot have same behavior with mutating as in classes.

This is my attempt:

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

Here is my take at it.

As this is an enum and not a class, you have to think different(TM): it is your description that has to change when the "state" of your enum changes (as pointed out by @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

Hope that helps.

Another option is for adjust() to flip between cases as follows:

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
}
}

Here's another approach, using only the knowledge gained from the tour until that point*

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

If you want to have adjust() act as a toggle (although there's nothing to suggest this is the case), use:

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

*(Although it doesn't explicitly mention how to specify a return type and a protocol)

It is a link about enum in swift.

Structures and enumerations are value types. By default, the properties of a value type cannot be modified from within its instance methods. link

Then, you have to use mutating function.

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

Here's a solution that doesn't change the current enum value, but their instance values instead (just in case it is useful to anyone).

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

Here's building on Jack's answer:

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
}
}
}

here's my code

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

My first contribution here:

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

Thanks for others!

This experiment threw me off too, due to the previous SimpleClass and SimpleStructure examples showing the property simpleDescription being modified internally, which caused me to think that I needed to do the same thing. After looking over the other answers posted here and reading the official Apple Swift 2.1 documentation, I came up with this:

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

Also notice that in the examples given by Apple for SimpleClass and SimpleStructure prior to this experiment, the simple description is lost internally - you cannot get the original value back (unless of course you save it outside of the class/structure); this is what prompted me to create a restore() method for the SimpleEnum example, which allows you to toggle it back and forth between values. Hope this is useful to someone!

I was thinking that the goal is simply to retain state and use a description to make the current state easier to read:

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

how about this

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"

I came up with this

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

Another variation: Using associated values to hold and display previous option (of the form "Selected 1, adjusted from 2, adjusted from 1, adjusted from 2, adjusted from 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"