จะรับชื่อค่าการแจงนับใน Swift ได้อย่างไร


167

หากฉันมีการแจงนับที่มีIntegerค่าดิบ:

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

let city = City.Melbourne

ฉันจะแปลงcityค่าเป็นสตริงได้Melbourneอย่างไร มีการพิมพ์วิปัสสนาชื่อประเภทนี้ในภาษาหรือไม่?

บางอย่างเช่น (รหัสนี้จะไม่ทำงาน):

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

คำตอบ:


139

ในฐานะของ Xcode 7 Beta 5 (สวิฟท์รุ่นที่ 2) ตอนนี้คุณสามารถพิมพ์ชื่อประเภทและกรณี enum เริ่มต้นโดยใช้print(_:)หรือแปลงเพื่อStringใช้Stringเป็นinit(_:)ค่าเริ่มต้นหรือสตริงแก้ไขไวยากรณ์ ดังนั้นสำหรับตัวอย่างของคุณ:

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"

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

debugPrint(_:)& String(reflecting:)สามารถใช้สำหรับชื่อที่ผ่านการรับรองโดยสมบูรณ์:

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

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

โปรดทราบว่าคุณสามารถกำหนดสิ่งที่พิมพ์ในแต่ละสถานการณ์เหล่านี้:

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

(ฉันไม่พบวิธีเรียกค่านี้ "ค่าเริ่มต้น" ตัวอย่างเช่นการพิมพ์ "เมืองคือเมลเบิร์น" โดยไม่ต้องหันกลับไปใช้คำสั่งสลับใช้\(self)ในการใช้description/ debugDescriptionทำให้เกิดการเรียกซ้ำแบบไม่สิ้นสุด)


ความเห็นข้างต้นString's init(_:)& init(reflecting:)initializers อธิบายว่าสิ่งที่พิมพ์ขึ้นอยู่กับสิ่งที่สะท้อนสอดชนิด:

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


ดูบันทึกประจำรุ่นสำหรับข้อมูลเกี่ยวกับการเปลี่ยนแปลงนี้


8
นอกจากนี้หากคุณต้องการค่าสตริงโดยไม่ใช้print(enum)คุณสามารถใช้String(enum)
Kametrixom

44
จับที่สำคัญนี้เท่านั้นทำงานสำหรับ enums สวิฟท์ หากคุณแท็ก @objc เพื่ออนุญาตการเชื่อมโยงการสนับสนุนบน OS X สิ่งนี้จะไม่ทำงาน
Claus Jørgensen

11
คำตอบเฉพาะที่ยอดเยี่ยมอย่างรวดเร็ว อย่างไรก็ตามหากคุณต้องการทำสิ่งนี้บน enum ที่ไม่รวดเร็วเช่นการพิมพ์ค่า (วัตถุประสงค์ C) CLAuthorizationStatusenum ในการlocationManager didChangeAuthorizationStatusติดต่อกลับของผู้แทนคุณต้องกำหนดส่วนขยายโปรโตคอล ตัวอย่างเช่น: extension CLAuthorizationStatus: CustomStringConvertable { public var description: String { switch self { case .AuthorizedAlways: return "AuthorizedAlways" <etc> } } }- เมื่อคุณทำสิ่งนี้แล้วมันจะทำงานได้ตามที่คุณคาดหวัง: พิมพ์ ("สถานะการตรวจสอบ: (\ สถานะ))"
Jeffro

3
"ณ Xcode 7 beta 5" ไม่มีความหมาย ไม่ใช่ Xcode ที่กำหนดสิ่งนี้เป็นคอมไพเลอร์ Swift และ Swift Runtime Libaries ฉันสามารถใช้ Xcode 9.3 แต่รหัสของฉันยังคงเป็น Swift 3 และฉันจะไม่สามารถใช้คุณสมบัติ Swift 4 ได้ การใช้ Xcode 9.3 รหัสนี้ใช้งานไม่ได้แม้ว่า Xcode 9.3 จะใหม่กว่า Xcode 7 มาก
Mecki

8
ฉันได้รับ initializer 'init (_ :)' ต้องการให้เมืองนั้นเป็นไปตาม 'LosslessStringConvertible' บน xcode 10.2, Swift 5. เราจะทำอย่างไรตอนนี้
rockgecko

73

ไม่มีการใคร่ครวญคดี enum ในขณะนี้ คุณจะต้องประกาศด้วยตนเอง:

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

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

หากคุณต้องการประเภท raw เพื่อเป็น Int คุณจะต้องทำการเปลี่ยนตัวเอง:

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
Noob คำถาม แต่ทำไมได้รับ {return self.rawValue} แทนที่จะส่งคืน self.value ฉันลองหลังและใช้งานได้ดี
Chuck Krutsinger

นอกจากนี้คุณยังสามารถละเว้นget { ... }ส่วนของความกะทัดรัดได้หากคุณไม่ได้กำหนด setter
iosdude

1
ขอบคุณสำหรับคำตอบที่ดี ใน Xcode 7.3 ฉันจะได้รับ: "Printable ถูกเปลี่ยนชื่อเป็น CustomStringConvertible" การแก้ปัญหาคือง่าย - enum City : String, CustomStringConvertible {ในรหัสตัวอย่างแรกข้างต้นเปลี่ยนบรรทัดแรกที่ ในฐานะที่เป็นส่วนหนึ่งของโพรโทคอล CSC จากนั้นคุณจะต้องเปลี่ยนคุณสมบัติให้เป็นแบบสาธารณะเช่น:public var description : String {
Jeffro

44

ใน Swift-3 (ทดสอบด้วย Xcode 8.1) คุณสามารถเพิ่มวิธีการต่อไปนี้ใน enum ของคุณ:

/**
 * 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) }
}

จากนั้นคุณสามารถใช้มันเป็นการเรียกใช้เมธอดปกติบนอินสแตนซ์ enum ของคุณ อาจใช้งานได้ใน Swift เวอร์ชันก่อนหน้า แต่ฉันยังไม่ได้ทดสอบ

ในตัวอย่างของคุณ:

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"

หากคุณต้องการมอบฟังก์ชั่นนี้ให้กับ enums ทั้งหมดของคุณคุณสามารถทำให้มันเป็นส่วนเสริม:

/**
 * 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) }
  }
}

ใช้งานได้กับ Swift เท่านั้น


18

สำหรับ Objective-C enums วิธีเดียวที่ในปัจจุบันดูเหมือนว่าจะเป็นเช่นการขยาย enum ด้วยการCustomStringConvertibleลงท้ายด้วยสิ่งที่ชอบ:

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

แล้วหล่อenumเป็นString:

String(UIDevice.currentDevice().batteryState)

12

String(describing:)initializer สามารถนำมาใช้เพื่อกลับกรณีชื่อฉลากแม้สำหรับ enums กับ rawValues ไม่ใช่สตริง:

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

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

โปรดทราบว่านี่จะไม่ทำงานหาก enum ใช้@objcตัวปรับแต่ง:

https://forums.swift.org/t/why-is-an-enum-returning-enumname-rather-than-caselabel-for-string-describing/27327

สร้างอินเตอร์เฟส Swift สำหรับประเภท Objective-C บางครั้งไม่รวม@objcตัวแก้ไข อย่างไรก็ตาม Enums เหล่านั้นถูกกำหนดไว้ใน Objective-C และดังนั้นจึงไม่ทำงานเหมือนด้านบน


7

ด้านบนของการสนับสนุน String (…) (CustomStringConvertible) สำหรับ enums ใน Swift 2.2 มีการสนับสนุนการสะท้อนกลับที่ค่อนข้างหัก สำหรับกรณี enum ที่มีค่าที่เกี่ยวข้องเป็นไปได้ที่จะได้รับป้ายชื่อของกรณี enum โดยใช้การสะท้อน:

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"

ด้วยการถูกทำลายฉันหมายถึงว่า "ง่าย" สำหรับ enums labelทรัพย์สินที่คำนวณโดยใช้การสะท้อนกลับจะส่งคืนnil(boo-hoo)

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

สถานการณ์ที่มีการสะท้อนควรเริ่มดีขึ้นหลังจาก Swift 3 วิธีแก้ปัญหาสำหรับในขณะนี้คือString(…)ตามที่แนะนำในหนึ่งในคำตอบอื่น ๆ :

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

2
ดูเหมือนว่าจะใช้งาน Swift 3.1 ได้โดยไม่จำเป็นต้องเลือก: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

มันน่าผิดหวังมาก

สำหรับกรณีที่คุณต้องการชื่อเหล่านั้น (คอมไพเลอร์รู้ดีว่าสะกดคำที่แน่นอน แต่ปฏิเสธที่จะให้การเข้าถึง - ขอบคุณทีม Swift !! -) แต่ไม่ต้องการหรือไม่สามารถสร้างฐานของ Enum ของคุณได้ verbose ทางเลือกที่ยุ่งยากมีดังนี้:

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

คุณสามารถใช้ข้างต้นดังต่อไปนี้:

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

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

และคุณจะได้รับผลลัพธ์ที่คาดหวัง (รหัสสำหรับคอลัมน์ที่คล้ายกัน แต่ไม่แสดง)

fetching element Title, column: Collections, row: 0

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

ทีม Swift ต้องได้รับคำสั่งจริงๆ พวกเขาสร้าง enum ที่คุณไม่สามารถทำได้enumerateและสิ่งที่คุณสามารถใช้ได้enumerateคือ "ลำดับ" แต่ไม่ใช่enum!


ดูเหมือนว่าจะค่อนข้างยืดเยื้อกว่าเพียงแค่ทำคำอธิบายตอบแทน String (สะท้อนถึง: ตนเอง)
Boon

4

ฉันชนเข้ากับคำถามนี้และต้องการแบ่งปันวิธีง่ายๆในการสร้าง magicFunction ที่กล่าวถึง

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

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

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

3

สวิฟท์ขณะนี้มีสิ่งที่เรียกว่าได้รับมอบหมายโดยปริยายราคาวัตถุดิบ โดยทั่วไปถ้าคุณไม่ให้ค่าดิบกับแต่ละเคสและ enum เป็นชนิดสตริงมันจะอนุมานว่าค่าดิบของเคสนั้นอยู่ในรูปแบบสตริง ลองทำดู

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

let city = City.Melbourne.rawValue

// city is "Melbourne"

3

สำหรับ 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"
        }
    }

}

หากตัวแปร "batteryState" ของคุณโทรหาแล้ว:

self.batteryState.description

1

เรียบง่าย แต่ใช้งานได้ ...

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")
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.