Swift enum với bộ khởi tạo tùy chỉnh mất bộ khởi tạo rawValue


95

Tôi đã cố gắng tổng hợp vấn đề này xuống dạng đơn giản nhất với những điều sau.

Thiết lập

Phiên bản Xcode 6.1.1 (6A2008a)

Một enum được định nghĩa trong 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
        }
    }
}

và mã mà khởi enum trong tập tin khác, 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")
    }
}

lỗi

Xcode cho tôi lỗi sau khi cố gắng khởi tạo MyEnumvới bộ khởi tạo giá trị thô của nó:

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

Ghi chú

  1. Theo Hướng dẫn ngôn ngữ Swift :

    Nếu bạn xác định một kiểu liệt kê có kiểu giá trị thô, kiểu liệt kê sẽ tự động nhận một trình khởi tạo lấy giá trị kiểu của giá trị thô (như một tham số được gọi rawValue) và trả về một thành viên liệt kê hoặc nil.

  2. Trình khởi tạo tùy chỉnh cho MyEnumđược xác định trong một tiện ích mở rộng để kiểm tra xem liệu trình khởi tạo giá trị thô của enum có bị xóa vì trường hợp sau từ Hướng dẫn ngôn ngữ hay không . Tuy nhiên, nó đạt được cùng một kết quả lỗi.

    Lưu ý rằng nếu bạn xác định bộ khởi tạo tùy chỉnh cho một loại giá trị, bạn sẽ không còn quyền truy cập vào bộ khởi tạo mặc định (hoặc bộ khởi tạo thành viên, nếu nó là một cấu trúc) cho loại đó. [...]
    Nếu bạn muốn loại giá trị tùy chỉnh của mình có thể khởi tạo bằng bộ khởi tạo mặc định và bộ khởi tạo thành viên, và cả với bộ khởi tạo tùy chỉnh của riêng bạn, hãy viết bộ khởi tạo tùy chỉnh của bạn trong một tiện ích mở rộng thay vì là một phần của triển khai ban đầu của loại giá trị.

  3. Di chuyển định nghĩa enum để MyClass.swiftgiải quyết lỗi cho barnhưng không cho foo.

  4. Xóa trình khởi tạo tùy chỉnh sẽ giải quyết được cả hai lỗi.

  5. Một cách giải quyết là đưa hàm sau vào định nghĩa enum và sử dụng nó thay cho bộ khởi tạo giá trị thô được cung cấp. Vì vậy, có vẻ như việc thêm trình khởi tạo tùy chỉnh có tác dụng tương tự như đánh dấu trình khởi tạo giá trị thô private.

    init?(raw: Int) {
        self.init(rawValue: raw)
    }
  6. Việc khai báo rõ ràng sự tuân thủ của giao thức RawRepresentabletrong MyClass.swiftgiải quyết lỗi nội tuyến cho bar, nhưng dẫn đến lỗi trình liên kết về các ký hiệu trùng lặp (vì các enums kiểu raw-value hoàn toàn phù hợp với RawRepresentable).

    extension MyEnum: RawRepresentable {}

Bất cứ ai có thể cung cấp thêm một chút thông tin chi tiết về những gì đang xảy ra ở đây? Tại sao không thể truy cập trình khởi tạo giá trị thô?


Bạn nên gửi một lỗi về điều này - các trình khởi tạo mặc định phải có internalphạm vi (hoặc ít nhất là phù hợp với loại), không private.
Nate Cook

Tôi đang gặp chính xác cùng một vấn đề. Khi tôi tạo ra một phong tục khởi tạo một mặc định là biến mất
Yariv Nissim

Đối với tôi có mùi như một con bọ.
akashivskyy

2
Cảm ơn vì đã xác thực những nghi ngờ của tôi. Đây là một lỗi.
nickgraef

Số 5 đã làm điều đó cho tôi.
Andrew Duncan

Câu trả lời:


25

Lỗi này đã được giải quyết trong Xcode 7 và Swift 2


24
Các câu trả lời thuộc loại này sinh lợi từ một liên kết đến vé được liên kết để khách truy cập trong tương lai có thể kiểm tra trạng thái của vấn đề.
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)
        }
    }
}

Trong trường hợp của bạn, điều này sẽ dẫn đến phần mở rộng sau:

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

7

Bạn thậm chí có thể làm cho mã đơn giản hơn và hữu ích mà không có switchtrường hợp, bằng cách này, bạn không cần thêm nhiều trường hợp hơn khi thêm một loại mới.

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

Vâng, đây là một vấn đề khó chịu. Tôi hiện đang giải quyết vấn đề này bằng cách sử dụng một hàm phạm vi toàn cầu hoạt động như một nhà máy, tức là

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

0

Điều này hoạt động cho Swift 4 trên Xcode 9.2 cùng với EnumSequence của tôi :

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

Đầu ra

A for Apple
C for Cat
F for Fun

-1

Thêm cái này vào mã của bạn:

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

Bạn có thể mở rộng Int thay thế không? Có vẻ như nó dễ dàng hơn.
ericgu
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.