【问题标题】:Swift enum with custom initializer loses rawValue initializer带有自定义初始化程序的 Swift 枚举丢失 rawValue 初始化程序
【发布时间】:2015-02-08 01:20:52
【问题描述】:

我已尝试通过以下方式将此问题归结为最简单的形式。

设置

Xcode 版本 6.1.1 (6A2008a)

MyEnum.swift 中定义的枚举:

internal enum MyEnum: Int {
    case Zero = 0, One, Two
}

extension MyEnum {
    init?(string: String) {
        switch string.lowercaseString {
        case "zero": self = .Zero
        case "one": self = .One
        case "two": self = .Two
        default: return nil
        }
    }
}

以及在另一个文件MyClass.swift中初始化枚举的代码:

internal class MyClass {
    let foo = MyEnum(rawValue: 0)  // Error
    let fooStr = MyEnum(string: "zero")

    func testFunc() {
        let bar = MyEnum(rawValue: 1)  // Error
        let barStr = MyEnum(string: "one")
    }
}

错误

Xcode 在尝试使用其原始值初始化程序初始化 MyEnum 时出现以下错误:

Cannot convert the expression's type '(rawValue: IntegerLiteralConvertible)' to type 'MyEnum?'

注意事项

  1. the Swift Language Guide:

    如果您使用原始值类型定义枚举,则该枚举会自动接收一个初始值设定项,该初始化程序采用原始值类型的值(作为称为rawValue 的参数)并返回枚举成员或nil

  2. MyEnum 的自定义初始值设定项在扩展中定义,以测试枚举的原始值初始值设定项是否因为来自the Language Guide 的以下情况而被删除。但是,它实现了相同的错误结果。

    请注意,如果您为值类型定义自定义初始化程序,您将无法再访问该类型的默认初始化程序(或成员初始化程序,如果它是结构)。 [...]
    如果您希望自定义值类型可以使用默认初始化程序和成员初始化程序以及您自己的自定义初始化程序进行初始化,请将自定义初始化程序编写在扩展中,而不是作为值类型原始实现的一部分。

  3. 将枚举定义移动到MyClass.swift 可以解决bar 的错误,但不能解决foo 的错误。

  4. 删除自定义初始化程序可以解决这两个错误。

  5. 一种解决方法是在枚举定义中包含以下函数,并使用它来代替提供的原始值初始值设定项。因此,添加自定义初始化程序似乎与标记原始值初始化程序 private 具有类似的效果。

    init?(raw: Int) {
        self.init(rawValue: raw)
    }
    
  6. MyClass.swift 中显式声明协议符合RawRepresentable 可解决bar 的内联错误,但会导致有关重复符号的链接器错误(因为原始值类型枚举隐式符合RawRepresentable)。

    extension MyEnum: RawRepresentable {}
    

谁能提供更多关于这里发生的事情的见解?为什么原始值初始化器不可访问?

【问题讨论】:

  • 你应该提交一个错误 - 默认初始化程序应该有internal 范围(或至少匹配类型),而不是private
  • 我遇到了完全相同的问题。一旦我创建了一个自定义初始化程序,默认初始化程序就消失了
  • 我闻起来像个虫子。
  • 感谢您验证我的怀疑。这已作为错误提交。
  • 5 号为我做了。

标签: swift enums


【解决方案1】:

这适用于 Xcode 9.2 上的 Swift 4 以及我的 EnumSequence

enum Word: Int, EnumSequenceElement, CustomStringConvertible {
    case apple, cat, fun

    var description: String {
        switch self {
        case .apple:
            return "Apple"
        case .cat:
            return "Cat"
        case .fun:
            return "Fun"
        }
    }
}

let Words: [String: Word] = [
    "A": .apple,
    "C": .cat,
    "F": .fun
]

extension Word {
    var letter: String? {
        return Words.first(where: { (_, word) -> Bool in
            word == self
        })?.key
    }

    init?(_ letter: String) {
        if let word = Words[letter] {
            self = word
        } else {
            return nil
        }
    }
}

for word in EnumSequence<Word>() {
    if let letter = word.letter, let lhs = Word(letter), let rhs = Word(letter), lhs == rhs {
        print("\(letter) for \(word)")
    }
}

输出

A for Apple
C for Cat
F for Fun

【讨论】:

    【解决方案2】:

    您甚至可以在没有switch case 的情况下使代码更简单和有用,这样您在添加新类型时就不需要添加更多 case。

    enum VehicleType: Int, CustomStringConvertible {
        case car = 4
        case moped = 2
        case truck = 16
        case unknown = -1
    
        // MARK: - Helpers
    
        public var description: String {
            switch self {
            case .car: return "Car"
            case .truck: return "Truck"
            case .moped: return "Moped"
            case .unknown: return "unknown"
            }
        }
    
        static let all: [VehicleType] = [car, moped, truck]
    
        init?(rawDescription: String) {
            guard let type = VehicleType.all.first(where: { description == rawDescription })
                else { return nil }
            self = type
        }
    }
    

    【讨论】:

      【解决方案3】:

      这个错误在 Xcode 7 和 Swift 2 中得到解决

      【讨论】:

      • 此类答案可从相关票证的链接中获益,以便未来的访问者可以查看问题的状态。
      【解决方案4】:

      是的,这是一个烦人的问题。我目前正在使用充当工厂的全局范围函数来解决它,即

      func enumFromString(string:String) -> MyEnum? {
          switch string {
          case "One" : MyEnum(rawValue:1)
          case "Two" : MyEnum(rawValue:2)
          case "Three" : MyEnum(rawValue:3)
          default : return nil
          }
      }
      

      【讨论】:

        【解决方案5】:
        extension TemplateSlotType {
            init?(rawString: String) {
                // Check if string contains 'carrousel'
                if rawString.rangeOfString("carrousel") != nil {
                    self.init(rawValue:"carrousel")
                } else {
                    self.init(rawValue:rawString)
                }
            }
        }
        

        在您的情况下,这将导致以下扩展:

        extension MyEnum {
            init?(string: String) {
                switch string.lowercaseString {
                case "zero": 
                    self.init(rawValue:0)
                case "one": 
                    self.init(rawValue:1)
                case "two":
                    self.init(rawValue:2)
                default: 
                    return nil
                }
            }
        }
        

        【讨论】:

          【解决方案6】:

          将此添加到您的代码中:

          extension MyEnum {
              init?(rawValue: Int) {
                  switch rawValue {
                  case 0: self = .Zero
                  case 1: self = .One
                  case 2: self = .Two
                  default: return nil
                  }
              }
          }
          

          【讨论】:

          • 你可以扩展 Int 吗?似乎更容易。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-08-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-05-16
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多