Swift enum พร้อมตัวเริ่มต้นที่กำหนดเองจะสูญเสียค่าเริ่มต้น rawValue


97

ฉันได้พยายามแก้ไขปัญหานี้ให้เป็นรูปแบบที่ง่ายที่สุดดังต่อไปนี้

ติดตั้ง

Xcode เวอร์ชัน 6.1.1 (6A2008a)

enum ที่กำหนดไว้ใน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
        }
    }
}

และรหัสที่เริ่มต้น enum ในไฟล์อื่น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. ตามคู่มือภาษา Swift :

    หากคุณกำหนดนับมีประเภทดิบมูลค่านับได้รับการเริ่มต้นที่จะนำค่าของชนิดมูลค่าดิบ (เป็นพารามิเตอร์ที่เรียกว่าโดยอัตโนมัติrawValue) nilและผลตอบแทนทั้งที่เป็นสมาชิกหรือการแจงนับ

  2. กำหนดเองสำหรับการเริ่มต้นMyEnumถูกกำหนดไว้ในส่วนขยายเพื่อทดสอบว่า enum ของ initializer ดิบมูลค่าถูกลบออกเพราะกรณีต่อไปนี้จากคู่มือภาษา อย่างไรก็ตามมันได้ผลลัพธ์ข้อผิดพลาดเดียวกัน

    โปรดทราบว่าหากคุณกำหนดตัวเริ่มต้นแบบกำหนดเองสำหรับประเภทค่าคุณจะไม่สามารถเข้าถึงตัวเริ่มต้นเริ่มต้นได้อีกต่อไป (หรือตัวเริ่มต้นแบบสมาชิกถ้าเป็นโครงสร้าง) สำหรับประเภทนั้น [... ]
    หากคุณต้องการให้ประเภทค่าที่กำหนดเองของคุณสามารถเริ่มต้นได้ด้วยตัวเริ่มต้นเริ่มต้นและตัวเริ่มต้นแบบสมาชิกและด้วยตัวเริ่มต้นที่คุณกำหนดเองให้เขียนตัวเริ่มต้นที่กำหนดเองในส่วนขยายแทนที่จะเป็นส่วนหนึ่งของการใช้งานดั้งเดิมของประเภทค่า

  3. ย้ายนิยาม enum เพื่อMyClass.swiftแก้ไขข้อผิดพลาดสำหรับแต่ไม่ได้สำหรับbarfoo

  4. การลบตัวเริ่มต้นที่กำหนดเองจะช่วยแก้ข้อผิดพลาดทั้งสองอย่าง

  5. วิธีแก้ปัญหาอย่างหนึ่งคือการรวมฟังก์ชันต่อไปนี้ในนิยาม enum และใช้แทนค่าเริ่มต้นค่าดิบที่ระบุ ดังนั้นจึงดูเหมือนว่าการเพิ่มที่กำหนดเองการเริ่มต้นมีผลคล้ายกับเครื่องหมาย privateinitializer

    init?(raw: Int) {
        self.init(rawValue: raw)
    }
    
  6. ประกาศอย่างชัดเจนว่าโปรโตคอลสอดคล้องกับRawRepresentableในMyClass.swiftแก้ไขข้อผิดพลาดแบบอินไลน์สำหรับbarแต่ผลในข้อผิดพลาดเกี่ยวกับลิงเกอร์สัญลักษณ์ที่ซ้ำกัน (เพราะดิบมูลค่า enums ประเภทโดยปริยายไปตามRawRepresentable)

    extension MyEnum: RawRepresentable {}
    

ใครช่วยให้ข้อมูลเชิงลึกเพิ่มเติมเกี่ยวกับสิ่งที่เกิดขึ้นที่นี่ได้ไหม เหตุใดเครื่องมือเริ่มต้นค่าดิบจึงไม่สามารถเข้าถึงได้


คุณควรจะยื่นข้อผิดพลาดเกี่ยวกับเรื่องนี้ - initializers เริ่มต้นควรจะมีinternalขอบเขต (หรืออย่างน้อยตรงกับประเภท) privateไม่ได้
Nate Cook

ฉันมีปัญหาเดียวกัน เมื่อฉันสร้างโปรแกรมเริ่มต้นที่กำหนดเองแล้วค่าเริ่มต้นจะหายไป
Yariv Nissim

มีกลิ่นเหมือนแมลงกับฉัน
akashivskyy

2
ขอขอบคุณที่ตรวจสอบความสงสัยของฉัน สิ่งนี้ถูกยื่นเป็นข้อบกพร่อง
nickgraef

หมายเลข 5 ทำเพื่อฉัน
Andrew Duncan

คำตอบ:


26

ข้อบกพร่องนี้ได้รับการแก้ไขแล้วใน Xcode 7 และ Swift 2


25
คำตอบของผลกำไรประเภทนี้จากลิงก์ไปยังตั๋วที่เกี่ยวข้องเพื่อให้ผู้เยี่ยมชมในอนาคตสามารถตรวจสอบสถานะของเรื่องได้
Raphael

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

8

คุณยังสามารถทำให้โค้ดง่ายขึ้นและมีประโยชน์โดยไม่ต้องใช้switchเคสด้วยวิธีนี้คุณไม่จำเป็นต้องเพิ่มกรณีเพิ่มเติมเมื่อคุณเพิ่มประเภทใหม่

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

1

ใช่นี่เป็นปัญหาที่น่ารำคาญ ฉันกำลังดำเนินการแก้ไขโดยใช้ฟังก์ชันขอบเขตทั่วโลกที่ทำหน้าที่เป็นโรงงานเช่น

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

1

สิ่งนี้ใช้ได้กับ Swift 4 บน Xcode 9.2 พร้อมกับ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

-1

เพิ่มสิ่งนี้ในรหัสของคุณ:

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

คุณสามารถขยาย Int แทนได้หรือไม่? ดูเหมือนง่ายกว่า
ericgu
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.