Trong Swift, có thể chuyển đổi một chuỗi thành một enum không?


93

Nếu tôi có một enum với các trường hợp a, b, c, d thì tôi có thể ép kiểu chuỗi "a" là enum được không?


3
Những 'phôi' này được gọi là chuyển đổi theo nghĩa đen.
Vatsal Manot

Câu trả lời:


136

Chắc chắn rồi. Enums có thể có một giá trị thô. Để trích dẫn các tài liệu:

Giá trị thô có thể là chuỗi, ký tự hoặc bất kỳ kiểu số nguyên hoặc dấu phẩy động nào

- Trích từ: Apple Inc. “Ngôn ngữ lập trình Swift.” iBooks. https://itun.es/us/jEUH0.l ,

Vì vậy, bạn có thể sử dụng mã như sau:

enum StringEnum: String 
{
  case one = "one"
  case two = "two"
  case three = "three"
}

let anEnum = StringEnum(rawValue: "one")!

print("anEnum = \"\(anEnum.rawValue)\"")

Lưu ý: Bạn không cần phải viết = "một", v.v. sau mỗi trường hợp. Các giá trị chuỗi mặc định giống với tên trường hợp, vì vậy việc gọi .rawValuesẽ chỉ trả về một chuỗi

BIÊN TẬP

Nếu bạn cần giá trị chuỗi để chứa những thứ như khoảng trắng không hợp lệ như một phần của giá trị chữ hoa thì bạn cần đặt chuỗi một cách rõ ràng. Vì thế,

enum StringEnum: String 
{
  case one
  case two
  case three
}

let anEnum = StringEnum.one
print("anEnum = \"\(anEnum)\"")

cho

anEnum = "một"

Nhưng nếu bạn muốn case onehiển thị "giá trị một", bạn sẽ cần cung cấp các giá trị chuỗi:

enum StringEnum: String 
{
  case one   = "value one"
  case two   = "value two"
  case three = "value three"
}

Giá trị thô phải có thể chuyển đổi theo nghĩa đen. Bạn không thể chỉ sử dụng bất kỳ Hashableloại nào .
Vatsal Manot

1
Ok ... Tôi đã trích dẫn tài liệu của Apple, trong đó liệt kê các loại giá trị có thể được sử dụng làm giá trị thô enum. Chuỗi, câu hỏi của OP, là một trong những loại được hỗ trợ.
Duncan C

1
Hmm, hãy tưởng tượng case one = "uno". Bây giờ, làm thế nào để phân tích cú pháp "one"thành giá trị enum? (không thể sử dụng tài liệu thô, vì chúng được sử dụng để bản địa hóa)
Agent_L Ngày

Có thể bạn có thể khởi tạo Chuỗi thô khi khởi tạo tùy thuộc vào bản địa hóa ... hoặc đơn giản là có mỗi enum khác nhau cho một bản địa hóa khác nhau. Trong mọi trường hợp, toàn bộ mục đích của việc có một enum là để loại bỏ phần thô bên dưới tức là bản địa hóa. Một thiết kế mã tốt sẽ không truyền tham số "una" ở bất kỳ đâu mà dựa vào StringEnum.one
SkyWalker

5
Bạn không cần phải viết = "one"vv sau mỗi trường hợp. Các giá trị chuỗi mặc định giống như tên trường hợp.
emlai

38

Tất cả bạn cần là:

enum Foo: String {
   case a, b, c, d
}

let a = Foo(rawValue: "a")
assert(a == Foo.a)

let 💩 = Foo(rawValue: "💩")
assert(💩 == nil)

Về mặt kỹ thuật, đây không phải là câu trả lời đúng vì điều này kiểm tra giá trị thô. Trong ví dụ ở đây như đã cho, không có giá trị thô nào được chỉ định, vì vậy nó hoàn toàn khớp với tên trường hợp, nhưng nếu bạn có một enum với giá trị thô, điều này sẽ bị phá vỡ.
Mark A. Donohoe

26

Trong Swift 4.2, giao thức CaseIterable có thể được sử dụng cho một enum với rawValues, nhưng chuỗi phải khớp với các nhãn trường hợp enum:

enum MyCode : String, CaseIterable {

    case one   = "uno"
    case two   = "dos"
    case three = "tres"

    static func withLabel(_ label: String) -> MyCode? {
        return self.allCases.first{ "\($0)" == label }
    }
}

sử dụng:

print(MyCode.withLabel("one")) // Optional(MyCode.one)
print(MyCode(rawValue: "uno"))  // Optional(MyCode.one)

2
Đây là một câu trả lời tuyệt vời! Nó thực sự giải quyết câu hỏi.
Matt Rundle

3
Đây là câu trả lời duy nhất thực sự hoạt động như OP đã hỏi, đó là về tên trường hợp không phải giá trị thô. Câu trả lời tốt!
Mark A. Donohoe

1
Trong khi điều này hoạt động, nó là một điều rất ngớ ngẩn để làm. Vui lòng không dựa trên chức năng dựa trên tên của các trường hợp trong mã.
Sulthan

7
Anh ta còn phải làm gì nữa? Điều gì sẽ xảy ra nếu anh ta đang viết một enum vào cơ sở dữ liệu và sau đó cần truyền lại nó?
Joe

15

Trong trường hợp với một enum có kiểu Int, bạn có thể làm như vậy:

enum MenuItem: Int {
    case One = 0, Two, Three, Four, Five //... as much as needs

    static func enumFromString(string:String) -> MenuItem? {
        var i = 0
        while let item = MenuItem(rawValue: i) {
            if String(item) == string { return item }
            i += 1
        }
        return nil
    }
}

Và sử dụng:

let string = "Two"
if let item = MenuItem.enumFromString(string) {
    //in this case item = 1 
    //your code
} 

1
Thật là điên rồ khi bạn không thể chỉ sử dụng nội dung chức năng tương tự vào ngôn ngữ. Tôi có thể tưởng tượng bạn lưu trữ các giá trị trong JSON chẳng hạn bằng tên enum và sau đó khi phân tích cú pháp cần chuyển đổi chúng trở lại. Viết một enumFromStringphương pháp cho mỗi enum bạn sử dụng có vẻ điên rồ.
Peterdk

1
@Peterdk, vui lòng đề xuất phương án thay thế tốt nhất có thể. Giải pháp Igor thực sự chỉ làm việc cho tôi.
Hemang

@Hemang Nó hoạt động tốt, được rồi, nhưng giải pháp tốt hơn sẽ là hỗ trợ Swift để tự động thực hiện việc này. Thật điên rồ khi làm điều này theo cách thủ công cho mỗi enum. Nhưng có, điều này hoạt động.
Peterdk

@Peterdk, bạn có thể vui lòng thêm một câu trả lời riêng cho cùng một câu trả lời được không? Nó chắc chắn sẽ giúp ích cho tất cả mọi người ở đây.
Hemang,

1
Không có gì điên rồ khi Swift không hỗ trợ nó ngay từ đầu. Điều điên rồ là chức năng dựa trên tên của một loại. Khi giá trị thay đổi, bạn sẽ phải cấu trúc lại và đổi tên tất cả các cách sử dụng. Đây không phải là cách chính xác để giải quyết vấn đề này.
Sulthan

2

Mở rộng câu trả lời của Duncan C

extension StringEnum: StringLiteralConvertible {

    init(stringLiteral value: String){
        self.init(rawValue: value)!
    }

    init(extendedGraphemeClusterLiteral value: String) {
        self.init(stringLiteral: value)
    }

    init(unicodeScalarLiteral value: String) {
        self.init(stringLiteral: value)
    }
}

2

Swift 4.2:

public enum PaymentPlatform: String, CaseIterable {
    case visa = "Visa card"
    case masterCard = "Master card"
    case cod = "Cod"

    var nameEnum: String {
        return Mirror(reflecting: self).children.first?.label ?? String(describing: self)
    }

    func byName(name: String) -> PaymentPlatform {
        return PaymentPlatform.allCases.first(where: {$0.nameEnum.elementsEqual(name)}) ?? .cod
    }
}

1

Đối với Int enum và biểu diễn chuỗi của chúng, tôi khai báo enum như sau:

enum OrderState: Int16, CustomStringConvertible {

    case waiting = 1
    case inKitchen = 2
    case ready = 3

    var description: String {
        switch self {
        case .waiting:
            return "Waiting"
        case .inKitchen:
            return "InKitchen"
        case .ready:
            return "Ready"
        }
    }

    static func initialize(stringValue: String)-> OrderState? {
        switch stringValue {
        case OrderState.waiting.description:
            return OrderState.waiting
        case OrderState.inKitchen.description:
            return OrderState.inKitchen
        case OrderState.ready.description:
            return OrderState.ready

        default:
            return nil
        }
    }
}

Sử dụng:

order.orderState = OrderState.waiting.rawValue

let orderState = OrderState.init(rawValue: order.orderState)
let orderStateStr = orderState?.description ?? ""
print("orderStateStr = \(orderStateStr)")
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.