Enum cepat dengan penginisialisasi khusus kehilangan penginisialisasi rawValue

96

Saya telah mencoba meringkas masalah ini ke bentuk yang paling sederhana dengan berikut ini.

Mempersiapkan

Versi Xcode 6.1.1 (6A2008a)

Enum yang ditentukan di 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
        }
    }
}

dan kode yang menginisialisasi enum di file lain, 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")
    }
}

Kesalahan

Xcode memberi saya kesalahan berikut ketika mencoba menginisialisasi MyEnumdengan penginisialisasi nilai mentahnya:

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

Catatan

  1. Sesuai Panduan Bahasa Swift :

    Jika Anda mendefinisikan enumerasi dengan tipe nilai mentah, enumerasi secara otomatis menerima penginisialisasi yang mengambil nilai tipe nilai mentah (sebagai parameter yang dipanggil rawValue) dan mengembalikan anggota enumerasi atau nil.

  2. Penginisialisasi khusus untuk MyEnumditentukan dalam ekstensi untuk menguji apakah penginisialisasi nilai mentah enum telah dihapus karena kasus berikut dari Panduan Bahasa . Namun, itu mencapai hasil kesalahan yang sama.

    Perhatikan bahwa jika Anda menentukan penginisialisasi ubahsuaian untuk suatu jenis nilai, Anda tidak akan lagi memiliki akses ke penginisialisasi default (atau penginisialisasi beranggota, jika itu adalah struktur) untuk jenis itu. [...]
    Jika Anda ingin jenis nilai khusus Anda dapat diinisialisasi dengan penginisialisasi default dan penginisialisasi sesuai anggota, dan juga dengan penginisialisasi khusus Anda sendiri, tulis penginisialisasi kustom Anda dalam ekstensi, bukan sebagai bagian dari penerapan asli jenis nilai.

  3. Memindahkan definisi enum untuk MyClass.swiftmenyelesaikan error untuk bartetapi tidak untuk foo.

  4. Menghapus penginisialisasi kustom menyelesaikan kedua kesalahan.

  5. Salah satu solusinya adalah dengan menyertakan fungsi berikut dalam definisi enum dan menggunakannya sebagai pengganti penginisialisasi nilai mentah yang disediakan. Jadi, sepertinya menambahkan penginisialisasi khusus memiliki efek yang mirip dengan menandai penginisialisasi nilai mentah private.

    init?(raw: Int) {
        self.init(rawValue: raw)
    }
  6. Mendeklarasikan kesesuaian protokol secara eksplisit ke RawRepresentabledalam MyClass.swiftmenyelesaikan error sebaris untuk bar, tetapi menghasilkan error linker tentang simbol duplikat (karena enum tipe nilai mentah secara implisit menyesuaikan dengan RawRepresentable).

    extension MyEnum: RawRepresentable {}

Adakah yang bisa memberikan lebih banyak wawasan tentang apa yang terjadi di sini? Mengapa penginisialisasi nilai mentah tidak dapat diakses?

nickgraef.dll
sumber
Anda harus melaporkan bug pada ini - penginisialisasi default harus memiliki internalcakupan (atau setidaknya cocok dengan jenisnya), bukan private.
Nate Cook
Saya mengalami masalah yang persis sama. Setelah saya membuat penginisialisasi khusus, penginisialisasi default hilang
Yariv Nissim
Baunya seperti serangga bagiku.
akashivskyy
2
Terima kasih telah memvalidasi kecurigaan saya. Ini telah diajukan sebagai bug.
nickgraef
Nomor 5 melakukannya untuk saya.
Andrew Duncan

Jawaban:

26

Bug ini diselesaikan di Xcode 7 dan Swift 2

alcamla.dll
sumber
25
Jawaban semacam ini mendapatkan keuntungan dari tautan ke tiket terkait sehingga pengunjung di masa mendatang dapat memeriksa status masalah.
Raphael
14
extension TemplateSlotType {
    init?(rawString: String) {
        // Check if string contains 'carrousel'
        if rawString.rangeOfString("carrousel") != nil {
            self.init(rawValue:"carrousel")
        } else {
            self.init(rawValue:rawString)
        }
    }
}

Dalam kasus Anda, ini akan menghasilkan ekstensi berikut:

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

Anda bahkan dapat membuat kode lebih sederhana dan berguna tanpa switchcase, dengan cara ini Anda tidak perlu menambahkan case lagi saat Anda menambahkan tipe baru.

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
    }
}
carbonr
sumber
1

Ya, ini adalah masalah yang mengganggu. Saat ini saya sedang mengerjakannya menggunakan fungsi cakupan global yang bertindak sebagai pabrik, yaitu

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
    }
}
Abu
sumber
1

Ini berfungsi untuk Swift 4 di Xcode 9.2 bersama dengan EnumSequence saya :

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

Keluaran

A for Apple
C for Cat
F for Fun
mclam.dll
sumber
-1

Tambahkan ini ke kode Anda:

extension MyEnum {
    init?(rawValue: Int) {
        switch rawValue {
        case 0: self = .Zero
        case 1: self = .One
        case 2: self = .Two
        default: return nil
        }
    }
}
Tony Swiftguy
sumber
Bisakah Anda memperpanjang Int? Sepertinya lebih mudah.
ericgu