Làm cách nào để có được tên của giá trị liệt kê trong Swift?


167

Nếu tôi có một bảng liệt kê với Integercác giá trị thô :

enum City: Int {
  case Melbourne = 1, Chelyabinsk, Bursa
}

let city = City.Melbourne

Làm thế nào tôi có thể chuyển đổi một citygiá trị thành một chuỗi Melbourne? Đây có phải là một loại tên nội tâm có sẵn trong ngôn ngữ?

Một cái gì đó như (mã này sẽ không hoạt động):

println("Your city is \(city.magicFunction)")
> Your city is Melbourne

Câu trả lời:


139

Tính đến Xcode 7 beta 5 (Swift phiên bản 2) bây giờ bạn có thể in tên loại và các trường hợp enum theo mặc định sử dụng print(_:), hoặc chuyển đổi để Stringsử dụng String's init(_:)cú pháp khởi tạo hoặc chuỗi suy. Vì vậy, ví dụ của bạn:

enum City: Int {
    case Melbourne = 1, Chelyabinsk, Bursa
}
let city = City.Melbourne

print(city)
// prints "Melbourne"

let cityName = "\(city)"   // or `let cityName = String(city)`
// cityName contains "Melbourne"

Vì vậy, không còn cần phải xác định và duy trì chức năng tiện lợi chuyển đổi trên từng trường hợp để trả về một chuỗi ký tự. Ngoài ra, điều này hoạt động tự động cho bất kỳ enum nào, ngay cả khi không có loại giá trị thô nào được chỉ định.

debugPrint(_:)& String(reflecting:)có thể được sử dụng cho một tên đủ điều kiện:

debugPrint(city)
// prints "App.City.Melbourne" (or similar, depending on the full scope)

let cityDebugName = String(reflecting: city)
// cityDebugName contains "App.City.Melbourne"

Lưu ý rằng bạn có thể tùy chỉnh nội dung được in trong mỗi trường hợp sau:

extension City: CustomStringConvertible {
    var description: String {
        return "City \(rawValue)"
    }
}

print(city)
// prints "City 1"

extension City: CustomDebugStringConvertible {
    var debugDescription: String {
        return "City (rawValue: \(rawValue))"
    }
}

debugPrint(city)
// prints "City (rawValue: 1)"

(Tôi chưa tìm được cách gọi vào giá trị "mặc định" này, ví dụ, để in "Thành phố là Melbourne" mà không cần dùng đến câu lệnh chuyển đổi. Sử dụng \(self)trong việc thực hiện description/ debugDescriptiongây ra đệ quy vô hạn.)


Các ý kiến trên String's init(_:)& init(reflecting:)initializers mô tả chính xác những gì được in, tùy thuộc vào những gì các loại chiếu theo phản ánh tới:

extension String {
    /// Initialize `self` with the textual representation of `instance`.
    ///
    /// * If `T` conforms to `Streamable`, the result is obtained by
    ///   calling `instance.writeTo(s)` on an empty string s.
    /// * Otherwise, if `T` conforms to `CustomStringConvertible`, the
    ///   result is `instance`'s `description`
    /// * Otherwise, if `T` conforms to `CustomDebugStringConvertible`,
    ///   the result is `instance`'s `debugDescription`
    /// * Otherwise, an unspecified result is supplied automatically by
    ///   the Swift standard library.
    ///
    /// - SeeAlso: `String.init<T>(reflecting: T)`
    public init<T>(_ instance: T)

    /// Initialize `self` with a detailed textual representation of
    /// `subject`, suitable for debugging.
    ///
    /// * If `T` conforms to `CustomDebugStringConvertible`, the result
    ///   is `subject`'s `debugDescription`.
    ///
    /// * Otherwise, if `T` conforms to `CustomStringConvertible`, the result
    ///   is `subject`'s `description`.
    ///
    /// * Otherwise, if `T` conforms to `Streamable`, the result is
    ///   obtained by calling `subject.writeTo(s)` on an empty string s.
    ///
    /// * Otherwise, an unspecified result is supplied automatically by
    ///   the Swift standard library.
    ///
    /// - SeeAlso: `String.init<T>(T)`
    public init<T>(reflecting subject: T)
}


Xem ghi chú phát hành để biết thông tin về sự thay đổi này.


8
Ngoài ra nếu bạn muốn giá trị chuỗi mà không sử dụng, print(enum)bạn có thể sử dụngString(enum)
Kametrixom

44
Bắt quan trọng, điều này chỉ hoạt động cho Swift enums. Nếu bạn gắn thẻ @objc để cho phép hỗ trợ ràng buộc trên OS X, điều này sẽ không hoạt động.
Claus Jørgensen

11
Câu trả lời cụ thể của Swift; tuy nhiên, nếu bạn cần thực hiện điều này trên một enum không nhanh, chẳng hạn như để in CLAuthorizationStatusgiá trị của enum (Objective C) bên trong hàm locationManager didChangeAuthorizationStatusgọi lại ủy nhiệm của bạn , bạn cần xác định một phần mở rộng giao thức. Ví dụ: extension CLAuthorizationStatus: CustomStringConvertable { public var description: String { switch self { case .AuthorizedAlways: return "AuthorizedAlways" <etc> } } }- một khi bạn đã thực hiện việc này, nó sẽ hoạt động như bạn mong đợi: print ("Trạng thái xác thực: (\ status))".
Jeffro

3
"Kể từ Xcode 7 beta 5" là vô nghĩa. Đây không phải là Xcode định nghĩa bất kỳ thứ gì trong số này, đó là trình biên dịch Swift và Libaries Libaries. Tôi có thể sử dụng Xcode 9.3 nhưng Mã của tôi vẫn có thể là Swift 3 và sau đó tôi sẽ không thể sử dụng các tính năng của Swift 4. Sử dụng Xcode 9.3, mã này không hoạt động mặc dù Xcode 9.3 mới hơn nhiều so với Xcode 7.
Mecki

8
Tôi đã nhận được trình khởi tạo 'init (_ :)' yêu cầu Thành phố tuân thủ 'LosslessStringConvertible' trên xcode 10.2, Swift 5. Làm thế nào để chúng tôi làm điều đó bây giờ?
rockgecko

73

Hiện tại không có nội tâm về các trường hợp enum. Bạn sẽ phải khai báo chúng theo cách thủ công:

enum City: String, CustomStringConvertible {
    case Melbourne = "Melbourne"
    case Chelyabinsk = "Chelyabinsk"
    case Bursa = "Bursa"

    var description: String {
        get {
            return self.rawValue
        }
    }
}

Nếu bạn cần loại thô là Int, bạn sẽ phải tự chuyển đổi:

enum City: Int, CustomStringConvertible {
  case Melbourne = 1, Chelyabinsk, Bursa

  var description: String {
    get {
      switch self {
        case .Melbourne:
          return "Melbourne"
        case .Chelyabinsk:
          return "Chelyabinsk"
        case .Bursa:
          return "Bursa"
      }
    }
  }
}

2
Câu hỏi của Noob, nhưng tại sao lại đặt get {return self.rawValue} thay vì chỉ trả về self.value? Tôi đã thử sau và nó hoạt động tốt.
Chuck Krutsinger

Bạn cũng có thể bỏ qua get { ... }phần cho ngắn gọn nếu bạn không xác định một setter.
iosdude

1
Cảm ơn câu trả lời tuyệt vời. Trong Xcode 7.3, tôi nhận được: "Printable đã được đổi tên thành CustomStringConvertible". Giải pháp rất dễ dàng - trong ví dụ mã đầu tiên ở trên, hãy thay đổi dòng đầu tiên thành enum City : String, CustomStringConvertible {. Là một phần của giao thức CSC, sau đó bạn sẽ cần thay đổi thuộc tính thành công khai , ví dụ:public var description : String {
Jeffro

44

Trong Swift-3 (được thử nghiệm với Xcode 8.1), bạn có thể thêm các phương thức sau vào enum của mình:

/**
 * The name of the enumeration (as written in case).
 */
var name: String {
    get { return String(describing: self) }
}

/**
 * The full name of the enumeration
 * (the name of the enum plus dot plus the name as written in case).
 */
var description: String {
    get { return String(reflecting: self) }
}

Sau đó, bạn có thể sử dụng nó như một cuộc gọi phương thức bình thường trong ví dụ enum của bạn. Nó cũng có thể hoạt động trong các phiên bản Swift trước, nhưng tôi chưa thử nghiệm nó.

Trong ví dụ của bạn:

enum City: Int {
    case Melbourne = 1, Chelyabinsk, Bursa
    var name: String {
        get { return String(describing: self) }
    }
    var description: String {
        get { return String(reflecting: self) }
    }
}
let city = City.Melbourne

print(city.name)
// prints "Melbourne"

print(city.description)
// prints "City.Melbourne"

Nếu bạn muốn cung cấp chức năng này cho tất cả các enum của mình, bạn có thể biến nó thành một phần mở rộng:

/**
 * Extend all enums with a simple method to derive their names.
 */
extension RawRepresentable where RawValue: Any {
  /**
   * The name of the enumeration (as written in case).
   */
  var name: String {
    get { return String(describing: self) }
  }

  /**
   * The full name of the enumeration
   * (the name of the enum plus dot plus the name as written in case).
   */
  var description: String {
    get { return String(reflecting: self) }
  }
}

Điều này chỉ hoạt động cho Swift enums.


18

Ví dụ, đối với Objective-C enum, cách duy nhất dường như là mở rộng enum với CustomStringConvertiblekết thúc bằng một cái gì đó như:

extension UIDeviceBatteryState: CustomStringConvertible {
    public var description: String {
        switch self {
        case .Unknown:
            return "Unknown"
        case .Unplugged:
            return "Unplugged"
        case .Charging:
            return "Charging"
        case .Full:
            return "Full"
        }
    }
}

Và sau đó đúc enumnhư String:

String(UIDevice.currentDevice().batteryState)

12

Trình String(describing:)khởi tạo có thể được sử dụng để trả về tên nhãn trường hợp ngay cả đối với các enum với rawValues ​​không phải là String:

enum Numbers: Int {
    case one = 1
    case two = 2
}

let one = String(describing: Numbers.one) // "one"
let two = String(describing: Numbers.two) // "two"

Lưu ý rằng điều này không hoạt động nếu enum sử dụng công cụ @objcsửa đổi:

https://forums.swift.org/t/why-is-an-enum-retinating-enumname-rather-than-caselabel-for-opes-descriping/27327

Các giao diện Swift được tạo cho các loại Objective-C đôi khi không bao gồm bộ @objcsửa đổi. Những Enums này tuy nhiên được định nghĩa trong Objective-C, và do đó không hoạt động như trên.


7

Ngoài hỗ trợ Chuỗi (trên) (CustomStringConvertible) cho các enum trong Swift 2.2, cũng có một số hỗ trợ phản chiếu bị phá vỡ cho chúng. Đối với các trường hợp enum có giá trị liên quan, có thể lấy nhãn của trường hợp enum bằng phản xạ:

enum City {
    case Melbourne(String)
    case Chelyabinsk
    case Bursa

    var label:String? {
        let mirror = Mirror(reflecting: self)
        return mirror.children.first?.label
    }
}

print(City.Melbourne("Foobar").label) // prints out "Melbourne"

Tuy nhiên, do bị phá vỡ, tôi có nghĩa là đối với các enum "đơn giản", thuộc labeltính được tính toán dựa trên sự phản ánh ở trên chỉ trả về nil(boo-hoo).

print(City.Chelyabinsk.label) // prints out nil

Rõ ràng, tình hình với sự phản chiếu sẽ trở nên tốt hơn sau Swift 3. Tuy nhiên, giải pháp cho bây giờ là String(…), như được đề xuất trong một trong những câu trả lời khác:

print(String(City.Chelyabinsk)) // prints out Cheylabinsk

2
Điều này dường như hoạt động trên Swift 3.1 mà không cần phải làm cho nó tùy chọn:var label:String { let mirror = Mirror(reflecting: self); if let label = mirror.children.first?.label { return label } else { return String(describing:self) } }
David James

5

Điều này thật đáng thất vọng.

Đối với trường hợp khi bạn cần những tên đó (trình biên dịch hoàn toàn biết chính tả, nhưng từ chối cho phép truy cập - cảm ơn nhóm Swift của bạn !! -) nhưng không muốn hoặc không thể biến String thành cơ sở của enum của bạn, a dài dòng, thay thế rườm rà như sau:

enum ViewType : Int, Printable {

    case    Title
    case    Buttons
    case    View

    static let all = [Title, Buttons, View]
    static let strings = ["Title", "Buttons", "View"]

    func string() -> String {
        return ViewType.strings[self.rawValue]
    }

    var description:String {
        get {
            return string()
        }
    }
}

Bạn có thể sử dụng như trên:

let elementType = ViewType.Title
let column = Column.Collections
let row = 0

println("fetching element \(elementType), column: \(column.string()), row: \(row)")

Và bạn sẽ nhận được kết quả mong đợi (mã cho Cột tương tự, nhưng không được hiển thị)

fetching element Title, column: Collections, row: 0

Ở trên, tôi đã làm cho descriptiontài sản tham khảo lại stringphương pháp, nhưng đây là một vấn đề của hương vị. Cũng lưu ý rằng staticcác biến được gọi là cần phải có phạm vi đủ điều kiện theo tên của loại kèm theo của chúng, vì trình biên dịch quá mất trí nhớ và không thể tự nhớ lại bối cảnh ...

Nhóm Swift phải thực sự được chỉ huy. Họ đã tạo ra enum mà bạn không thể enumeratevà cái mà bạn có thể sử dụng enumeratelà "Chuỗi" nhưng không enum!


Điều đó có vẻ khá dài dòng hơn là chỉ thực hiện chuỗi trả về (phản ánh: bản thân) trong mô tả.
Boon

4

Tôi đã va vào câu hỏi này và muốn chia sẻ một cách đơn giản để tạo ra phép thuật được đề cập

enum City: Int {
  case Melbourne = 1, Chelyabinsk, Bursa

    func magicFunction() -> String {
        return "\(self)"
    }
}

let city = City.Melbourne
city.magicFunction() //prints Melbourne

3

Swift bây giờ có cái được gọi là Giá trị thô được gán ngầm định . Về cơ bản nếu bạn không đưa ra các giá trị thô cho từng trường hợp và enum thuộc kiểu String, nó sẽ suy ra rằng giá trị thô của trường hợp đó là ở định dạng chuỗi. Tiêp tục đi, hay thử xem.

enum City: String {
  case Melbourne, Chelyabinsk, Bursa
}

let city = City.Melbourne.rawValue

// city is "Melbourne"

3

Dành cho Swift:

extension UIDeviceBatteryState: CustomStringConvertible {

    public var description: String {
        switch self {
        case .unknown:
            return "unknown"
        case .unplugged:
            return "unplugged"
        case .charging:
            return "charging"
        case .full:
            return "full"
        }
    }

}

nếu biến của bạn là "pinState" thì hãy gọi:

self.batteryState.description

1

Đơn giản nhưng hoạt động ...

enum ViewType : Int {
    case    Title
    case    Buttons
    case    View
}

func printEnumValue(enum: ViewType) {

    switch enum {
    case .Title: println("ViewType.Title")
    case .Buttons: println("ViewType.Buttons")
    case .View: println("ViewType.View")
    }
}
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.