Swift枚举具有相同值的多个案例

时间:2015-01-20 04:30:15

标签: swift enums

在C中你可以让你的枚举有这个

typedef enum _Bar {
    A = 0,
    B = 0,
    C = 1
} Bar;

在swift中我想做同等的事情。但是编译器抱怨它不是唯一的。我如何告诉它我希望两个案例具有相同的值?

enum Bar : Int {
    case A = 0
    case B = 0 //Does not work
    case C = 1
}

我试过

case A | B = 0

case A, B = 0

但它似乎并不像我想要的那样有用。我感谢任何帮助。

8 个答案:

答案 0 :(得分:16)

Swift不支持重复值。 (或"别名"语义上)如果你不介意,你可以通过使用这样的东西来模仿它。

enum Foo: Int {
    case Bar = 0

    static var Baz:Foo {
        get {
            return  Bar
        }
    }
    static var Jar:Foo {
        get {
            return  Foo(rawValue: 0)!
        }
    }
}

答案 1 :(得分:5)

Swift不允许enum的元素共享值。从“原始值”标题下的枚举文档(强调我的):

  

原始值可以是字符串,字符或任何整数或浮点数类型。 每个原始值在其枚举声明中必须是唯一的。

答案 2 :(得分:4)

这是绕过它的另一种方式:

enum Animal {

  case dog
  case cat
  case mouse
  case zebra

  var description: String {
    switch self {
    case .dog:
      return "dog"

    case .cat:
      return "dog"

    case .mouse:
      return "dog"

    case .zebra:
      return "zebra"

    default:
      break
    }
  }
}

答案 3 :(得分:3)

我不确定你能不能。以下内容摘自Apple。

  

“与C和Objective-C不同,Swift枚举成员在创建时未分配默认整数值。在上面的CompassPoints示例中,North,South,East和West不会隐式地等于0,1,2和3.相反,不同的枚举成员本身就是完全成熟的值,具有显式定义的CompassPoint类型。 “

摘自:Apple Inc.“The Swift Programming Language。”iBooks。 https://itunes.apple.com/us/book/swift-programming-language/id881256329?mt=11

由于枚举成员不隐式地等于0,1等,因此编译器将每个成员视为唯一值。当你尝试复制它时,编译器会抱怨,因为它已经被创建了。

您是否有理由需要重复该值?

答案 4 :(得分:1)

我遇到了关于具有相同值的多个案例的相同问题。谷歌解决这个问题之后,我更喜欢computed properties enum which come from this post的以下解决方案。

Swift enum properties

答案 5 :(得分:1)

如何告诉我我希望两个案例具有相同的价值?

我要证明这对于所有 Swift版本都是可行的

这是 不是 的解决方法。实际上是可能的。即使他们说不是。

这确实可以回答您的问题:

enum Foo: Bool {
    case bar = 0
    case bas = 1
    case bat = 2
}

// THIS IS THE KEY!!
extension Bool: ExpressibleByIntegerLiteral {
    public init(integerLiteral value: Int) { self = true }
}

func bug() {
    assert(Foo.bar == Foo.bas) // This assertion is legitimate!
    assert(Foo.bar.rawValue == Foo.bas.rawValue) // This assertion is legitimate!
}

不可思议的是,枚举Foo上的所有个案的原始值均为true。您绝对可以重新设置它,以支持不仅仅是Bool的更多重复类型。

想象一下所有的影响。Set,Dictionary,switch语句,CaseIterable等。


我想我必须在bugs.swift.org中提交一个Swift Radar ... 或者我们可以将其保留为我们的小秘密

您可以按照SR-13212进行此错误修复的更新。

编辑: -他们将其保留为功能。


答案

您提到您想要一个原始值为0, 0, and 1的枚举。在这里!

enum Bar: Int {
    case A = "0"
    case B = "0 "
    case C = "1"
}

extension Int: ExpressibleByStringLiteral {
    public init(stringLiteral elements: String) {
        self = Int(String(elements.first ?? Character("0"))) ?? 0
    }
}

此扩展名采用每个String的第一个字符并将其转换为Int。因此,使原始值真正 0, 0, and 1

正确的说法:Bar.A == Bar.B

当前可在所有Swift版本上使用。


编辑: 例外

这是为什么我认为这是一个错误的另一个提示。 Double符合ExpressibleByIntegerLiteral。但是,此代码将无法编译:

enum Foo: Double {
    case a = 1.0
    case b = 1
}

尝试覆盖它会导致此错误:

  • 警告:类型模块Double中已经声明ExpressibleByIntegerLiteral与协议Swift的符合性

Swift工程师以某种方式阻止了此操作,但其他一切都没问题。

答案 6 :(得分:0)

我碰巧发现了这一点,很惊讶在任何答案中都没有Swift-y的黑魔法(当然还是不错的)。我个人不喜欢static属性,因为它们会在其他地方(例如,在模式匹配中。

我认为“最干净的”解决方案是依靠RawRepresentable并自己定义。然后,您可以反驳Swift不支持此功能的主要原因:init?(rawValue:)方法。仅通过将String指定为采用的原始值(即enum MyEnum: String { ... }),编译器就无法自行为其生成逻辑。由于没有定义足够的潜在原始值(因为一个或多个被加倍了),因此需要知道跳过哪种情况。自己采用该协议可以让您简单地为给定的原始值字符串选择一种情况作为“默认”,并基本上防止另一种情况用原始值进行构造(当然,常规构造仍然有效)。

不幸的是,这也有点开销,因为前case myCase = "its_raw_value"然后分布在

  • 常规情况(无原始值String):case myCase(定义)
  • init?(rawValue:)方法中的一个开关:... case "its_raw_value": return .myCase ...
  • 所需的rawValue属性中的一个开关:... case .myCase: return "its_raw_value" ...

我认为我有个不错的方法,可以减少一点并防止原始值浮动太多。我写了一个游乐场来说明,并将其简单粘贴在这里:


// define protocol and a default implementation
protocol SloppyRawRepresentable
where Self: RawRepresentable, Self.RawValue: Hashable, Self: CaseIterable {
}
extension SloppyRawRepresentable {
    init?(rawValue: RawValue) {
        var tempMapping = [RawValue: Self]()
        for oneCase in Self.allCases {
            // first come first served. any case after the first having the same
            // raw value is simply ignored, so the first is the "default"
            if tempMapping[oneCase.rawValue] == nil {
                tempMapping[oneCase.rawValue] = oneCase
            }
        }
        guard let candidate = tempMapping[rawValue] else { return nil }
        self = candidate
    }
}

// use it. Note we don't need the init
enum EventNames: SloppyRawRepresentable {
    typealias RawValue = String

    var rawValue: String {
        switch self {
        case .standardEvent: return "standard"
        case .joesConfig: return "iAmJoe"
        case .myConfig: return "iAmJoe"
        }
    }

    case standardEvent
    case joesConfig
    case myConfig
}

// some example output

print(EventNames.standardEvent)
print(EventNames.joesConfig)
print(EventNames.myConfig)

print(EventNames.standardEvent.rawValue)
print(EventNames.joesConfig.rawValue)
print(EventNames.myConfig.rawValue)

print(EventNames(rawValue: "standard")!)
print(EventNames(rawValue: "iAmJoe")!)
print(EventNames(rawValue: "iAmJoe")!)

print(EventNames(rawValue: "standard")!.rawValue)
print(EventNames(rawValue: "iAmJoe")!.rawValue)
print(EventNames(rawValue: "iAmJoe")!.rawValue)

它应该是不言自明的。案例定义和为其分配原始值仍然是分裂的,我认为没有办法解决,但可以派上用场。当然,所有这一切都需要约束RawValue,而我依靠枚举CaseIterable来轻松构造该辅助字典,但是我认为这应该是可以的,在性能上是明智的。

答案 7 :(得分:0)

免责声明:我不建议这样做。大多数人不会考虑最佳实践。有缺点,具体取决于应用程序。我们只说这些想法是理论性的,假设性的,不纯正的,只是出于沉思而提出的

一种可能性是添加某种毫无意义的微分器,以足以阻止Swift检查身份,这不会影响您对代码中的原始值所做的工作。

他是一个Float的示例,在音乐中,A♯(A尖音)与B♭(B平音)的频率相同。换句话说,它们都是同一事物的不同注释。

以微赫兹为单位的差异对于音频引擎而言毫无意义,但足以区分Swift枚举中的大小写值。换句话说,实际上,在大多数音乐应用程序中116.540和116.540001之间没有区别:

enum NoteFrequency : Float = {
   case A = 110.0, A_SHARP = 116.540, B_FLAT = 116.540001
}

从准容忍到更丑陋:对于仅分配和打印的字符串,您可以添加不可打印的字符来区分。

显然,这样做有弊端(可能很严重),如果您执行类似的操作,后来您(或其他人)可能不知道自己做了什么,并编写了无法解释的代码(例如在rawValue中使用)字符串比较中的枚举),并且其代码失败。要使它在String比较中起作用,您可能必须做一些事来忽略最后一个字符,或者在比较之前用myEnumString.rawValue.dropLast()将它变成不可打印的字符。

 enum Animals : String = {
     case CAT = "Feline\u{01}", LION = "Feline\u{02}"
 }