向现有枚举添加一个带协议的情况

25

我希望创建一个协议,该协议将强制所有符合该协议枚举类型遵循特定的命名规则。

例如,如果我有一个像这样的枚举类型

enum Foo{
    case bar(baz: String)
    case baz(bar: String)
}

我希望用一个协议来扩展它,该协议会添加另一种情况:

case Fuzz(Int)

这可行吗?

4个回答

46

设计

解决方法是使用带有静态变量的 struct

注意:这就是在 Swift 3 中为 Notification.Name 所做的。

以下是在 Swift 3 上的实现:

结构体:

struct Car : RawRepresentable, Equatable, Hashable, Comparable {

    typealias RawValue = String

    var rawValue: String

    static let Red  = Car(rawValue: "Red")
    static let Blue = Car(rawValue: "Blue")

    //MARK: Hashable

    var hashValue: Int {
        return rawValue.hashValue
    }

    //MARK: Comparable

    public static func <(lhs: Car, rhs: Car) -> Bool {

        return lhs.rawValue < rhs.rawValue
    }

}

协议

protocol CoolCar {

}

extension CoolCar {

    static var Yellow : Car {

        return Car(rawValue: "Yellow")
    }
}

extension Car : CoolCar {

}

调用

let c1 = Car.Red


switch c1 {
case Car.Red:
    print("Car is red")
case Car.Blue:
    print("Car is blue")
case Car.Yellow:
    print("Car is yellow")
default:
    print("Car is some other color")
}

if c1 == Car.Red {
    print("Equal")
}

if Car.Red > Car.Blue {
    print("Red is greater than Blue")
}

注意:

请注意,这种方法不能替代enum,仅在编译时不知道值的情况下使用。


为什么要将 protocol CoolCar 留空,然后再去扩展它呢? - Marty
这只是为了展示扩展是可能的,即使使用扩展,这个解决方案也可以工作。CoolCar 可能在一个框架中被定义(例如:标准库/Foundation),你可能无法直接修改它。但你可以通过扩展来实现,并且这个解决方案仍然可以工作。 - user1046037
1
我们怎样才能从这个结构体中实现类似于 Car.Red(parameter: String) 这样的调用方式? - Bishow Gurung
1
问题在于现在你需要始终为开关定义默认值。 - Roman Roba
那个开关不能与uitableview一起使用。如果我做同样的事情,并且需要在cellforrow中有开关来检查它是哪个部分,它将无法工作。 - Arun Kumar

13

不行,因为你不能在enum之外声明case


7
一个 扩展 可以添加一个嵌套的 枚举,如下所示:
enum Plants {
  enum Fruit {
     case banana
  }
} 


extension Plants {
  enum Vegetables {
     case potato
  }
}

如何将一个变量赋值为香蕉或土豆?该变量的类型是什么? - daniel

3
以下是一些额外的想法,可能对某些人有帮助:

以你的例子为例:

enum Foo {
    case bar(baz: String)
    case baz(bar: String)
} 

您可以考虑将其“嵌套”在您自己的枚举类型的case中:
enum FooExtended {
    case foo(Foo) // <-- Here will live your instances of `Foo`
    case fuzz(Int)
}

通过这种解决方案,访问与类型相关的“隐藏”用例变得更加费力。但是在某些应用程序中,这种简化实际上可能是有益的。

另一种选择是重新创建并扩展它,同时有一种方法将Foo转换为扩展的enumFooExtended(例如使用自定义init):

enum FooExtended {
    case bar(baz: String)
    case baz(bar: String)
    case fuzz(Int)

    init(withFoo foo: Foo) {
        switch foo {
        case .bar(let baz):
            self =  .bar(baz: baz)
        case .baz(let bar):
            self = .baz(bar: bar)
        }
    }
}

也许有很多地方,其中一个或两个解决方案都没有意义,但我相信它们对某些人可能会有用(即使只是作为练习)。


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