【问题标题】:Get enumeration name when using associated values使用关联值时获取枚举名称
【发布时间】:2016-02-13 00:36:49
【问题描述】:

我有一个枚举定义如下

enum Fruit {
    case Apple(associatedValue: String)
    case Orange(associatedValue: String)
}

我有一个接受 Fruit 类型参数的函数

func printNameOnly(fruit: Fruit) {

}

在此函数中,我想将枚举大小写为字符串,即我想获取字符串“Apple”或“Orange”,而不考虑相关值是什么。 Swift 有可能吗?

我显然可以编写一个函数,它接受水果枚举并使用 case 语句返回一个字符串,但我试图找到一种方法来避免这种情况,因为我想要的字符串是枚举 case 名称本身。

【问题讨论】:

  • 您可以添加与您的案例字符串相同的其他关联值。
  • 但是每次必须使用枚举时,我都必须指定一个字符串,例如 let v = .Apple("Apple")
  • 在 Swift 中,枚举的原始值和关联值不能共存。您可能想解释一下为什么要这样做,这可能会为您的问题提供另一种解决方案。

标签: swift enums


【解决方案1】:

试试这个(Swift 3.1)。涵盖相关或常规案例。

enum Fruit {
    case banana
    case apple(String)
    case orange(String)

    var label:String {
        let mirror = Mirror(reflecting: self)
        if let label = mirror.children.first?.label {
            return label
        } else {
            return String(describing:self)
        }
    }
}

print(Fruit.banana.label) // "banana"
print(Fruit.apple("yum").label) // "apple"

【讨论】:

    【解决方案2】:

    所以你想要一个带有 RawValue 关联值的 Swift 枚举。

    You can't

    我能想到的最佳解决方案是向您的枚举添加一个计算属性(类似于您在自己的问题中建议的)。

    enum Fruit {
        case Apple(name:String)
        case Orange(name:String)
    
        var fruitDesc: String {
            switch self {
            case .Apple: return "Apple"
            case .Orange: return "Orange"
            }
        }
    }
    
    let fruit = Fruit.Apple(name: "McIntosh")
    print(fruit.fruitDesc) // Apple
    

    【讨论】:

    • 我们可以有一个带有 RawValue 和关联值的 Swift 枚举,但如果我们这样做,我们需要小心,我试图在我的回答中指出这一点。
    【解决方案3】:

    如果您想获取枚举案例标签以进行调试,那么 Swift 运行时中的这个简洁的私有反射函数可能会派上用场:

    /// Returns the case label for the given enumeration value.
    public func getEnumCaseName<T>(_ value: T) -> String? {
        return __getEnumCaseName(value).flatMap { String(validatingUTF8: $0) }
    }
    
    /// A private Swift function from the compiler which returns the case
    /// label of the given enumeration value, represented as a C string.
    @_silgen_name("swift_EnumCaseName")
    fileprivate func __getEnumCaseName<T>(_ value: T) -> UnsafePointer<CChar>?
    

    我只用 Swift 4 测试了这种方法。如上所述,我不建议将其用于生产代码,而仅用于调试实用程序,因为它是私有/未记录的 API,并且可能会在未来的 Swift 版本中中断。

    【讨论】:

      【解决方案4】:

      当枚举具有关联值时,打印它们将列出所有值以及枚举名称。例如:

      let anApple = Fruit.Apple("myApple")
      print(anApple)
      

      这将产生:

      Apple("myApple")
      

      因此要获得“Apple”,将部分提取到第一个“(”。

      【讨论】:

      • 我认为依赖默认描述产生的字符串表示会非常hacky。
      • 也许吧,但是当您将所有枚举值转换为字符串时,您就是这样做的。您依靠 description 生成枚举名称。
      【解决方案5】:

      是的,反射 API 可以提供帮助。这个在调试和生产中都可以工作。

      enum Fruit {
          case apple(associatedValue: String)
          case orange(associatedValue: String)
      }
      
      func fruitNameOnly(fruit: Fruit) -> String {
          return Mirror(reflecting: fruit).children.first!.label!
      }
      
      let greenApple = Fruit.apple(associatedValue: "Green")
      
      print(fruitNameOnly(fruit: greenApple)) // apple
      

      使用两个相关的原始值的扩展版本:

      enum Fruit: RawRepresentable {
          case apple(associatedValue: String)
          case orange(associatedValue: String)
      
         typealias RawValue = String
          var rawValue: String {
      
              //return Mirror(reflecting: self).children.first!.label!
              // we rather use a regular switch this time which allows for custom values
              switch self {
              case .apple:     return "apple"
              case .orange(let av):   return "orange " + av // Edge case, careful!
              // Normally rawValues should form one to one relationship.
              // RawRepresentable protocol indicates, that you can "switch back and forth between a custom type and an associated RawValue type without losing the value of the original RawRepresentable type" (developer.apple.com/documentation/swift/rawrepresentable)
      
              }
          }
      
          init?(rawValue: String) {
              switch rawValue {
              case "apple":
                  self = .apple(associatedValue: "")
              case "orange":
                  self = .orange(associatedValue: "")
              default:
                  return nil
              }
          }
      }
      
      func fruitNameOnly(fruit: Fruit) -> String {
          return Mirror(reflecting: fruit).children.first!.label!
      }
      
      let greenApple = Fruit.apple(associatedValue: "green")
      print(fruitNameOnly(fruit: greenApple)) // apple
      
      if let yellowOrange = Fruit.init(rawValue: "orange") {
         print(yellowOrange) // orange(associatedValue: "")
      }
      print(greenApple.rawValue) //apple
      let redOrange = Fruit.orange(associatedValue: "red")
      print(redOrange.rawValue) //orange red
      

      【讨论】:

        猜你喜欢
        • 2019-02-07
        • 2013-04-08
        • 1970-01-01
        • 1970-01-01
        • 2012-12-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多