ฉันจะได้รับการนับ Swift enum ได้อย่างไร


186

ฉันจะกำหนดจำนวนผู้ป่วยใน Swift enum ได้อย่างไร

(ฉันต้องการหลีกเลี่ยงการระบุค่าทั้งหมดด้วยตนเองหรือใช้ " เคล็ดลับ enum_count " ด้วยตนเองหากเป็นไปได้)

คำตอบ:


173

ในฐานะของ Swift 4.2 (Xcode 10) คุณสามารถประกาศความสอดคล้องกับCaseIterableโปรโตคอลนี้ใช้ได้กับการแจกแจงทั้งหมดโดยไม่มีค่าที่เกี่ยวข้อง:

enum Stuff: CaseIterable {
    case first
    case second
    case third
    case forth
}

จำนวนกรณีที่ได้รับเพียงแค่ตอนนี้ด้วย

print(Stuff.allCases.count) // 4

สำหรับข้อมูลเพิ่มเติมโปรดดู


1
ในรุ่นล่าสุดของ swift ข้อผิดพลาดในการโยน "ประเภท 'DAFFlow' ไม่สอดคล้องกับโปรโตคอล 'RawRepresentable'" ทำไมมันบังคับให้ฉันทำตามนั้น? ความคิดใด ๆ
Satyam

@Satyam: DAFFlow คืออะไร
Martin R

ขอโทษฉันลืมที่จะพูดถึงว่า "DAFFlow 'เป็น enum ง่าย ๆ ที่ไม่ได้รับมรดกจากโปรโตคอลอื่น ๆ
Satyam

1
นี่เป็นทางออกที่ดีที่สุด แต่เพื่อความชัดเจน - นักพัฒนา Apple จะสามารถเริ่มใช้งานได้เมื่อ Xcode 10 (และ Swift 4.2) ออกมาจากเบต้า (น่าจะประมาณวันที่ 14 กันยายน 2018)
JosephH

1
@DaniSpringer: คุณจะพบรายละเอียดเต็มไปด้วยเลือดที่github.com/apple/swift-evolution/blob/master/proposals/... แต่โดยปกติแล้วคุณไม่ต้องการประเภทนั้นอย่างชัดเจนเนื่องจากการอนุมานประเภทอัตโนมัติของคอมไพเลอร์
Martin R

143

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

enum Reindeer: Int {
    case Dasher, Dancer, Prancer, Vixen, Comet, Cupid, Donner, Blitzen
    case Rudolph

    static let count: Int = {
        var max: Int = 0
        while let _ = Reindeer(rawValue: max) { max += 1 }
        return max
    }()
}

16
ในขณะที่ดีเพราะคุณไม่จำเป็นต้อง hardcode ค่านี้จะยกตัวอย่างทุกค่า enum ทุกครั้งที่มีการเรียก นั่นคือ O (n) แทน O (1) :(
รหัสผู้บัญชาการ

4
นี่เป็นทางออกที่ดีสำหรับ Int ที่ต่อเนื่องกัน ฉันชอบการดัดแปลงเล็กน้อย เปลี่ยนคุณสมบัติการนับแบบสแตติกเป็นวิธีการแบบคงที่ countCases () และกำหนดให้กับ caseCount ค่าคงที่แบบคงที่ซึ่งขี้เกียจและปรับปรุงประสิทธิภาพด้วยการโทรซ้ำ
Tom Pelaia

2
@ShamsAhmed: แปลง var ที่คำนวณเป็นแบบคงที่
Nate Cook

3
ถ้าคุณพลาดคุณค่าใน Enum ล่ะ เช่นcase A=1, B=3?
Sasho

2
มีข้อสันนิษฐาน 2 ข้อที่นอกเหนือจากการenumมีIntค่า raw ที่คุณลืมพูดถึง: Swift enum ที่มีค่า raw ดิบไม่จำเป็นต้องเริ่มต้นจาก 0 (แม้ว่าจะเป็นพฤติกรรมเริ่มต้น) และค่าดิบของมันอาจเป็นแบบสุ่มก็ได้ เพื่อเพิ่มขึ้น 1 (แม้ว่าจะเป็นพฤติกรรมเริ่มต้น)
DávidPásztor

90

อัปเดต Xcode 10

ใช้CaseIterableโปรโตคอลใน enum มันให้allCasesคุณสมบัติคงที่ซึ่งมีทุกกรณี enum เป็นCollectionเป็น เพียงใช้countทรัพย์สินเพื่อทราบจำนวน Enum ที่มีอยู่

ดูคำตอบของมาร์ตินเพื่อเป็นตัวอย่าง (และยกเลิกคำตอบของเขามากกว่าของฉัน)


คำเตือน : วิธีการด้านล่างดูเหมือนจะไม่ทำงานอีกต่อไป

ฉันไม่ทราบวิธีทั่วไปใด ๆ ในการนับจำนวนกรณี enum ฉันได้สังเกตเห็นว่าhashValueทรัพย์สินของคดี enum นั้นเพิ่มขึ้นเริ่มจากศูนย์และตามลำดับที่กำหนดโดยลำดับที่มีการประกาศกรณี ดังนั้นแฮชของ enum สุดท้ายบวกหนึ่งจะสอดคล้องกับจำนวนของกรณี

ตัวอย่างเช่นกับ enum นี้:

enum Test {
    case ONE
    case TWO
    case THREE
    case FOUR

    static var count: Int { return Test.FOUR.hashValue + 1}
}

count ส่งคืน 4

ฉันไม่สามารถบอกได้ว่านี่เป็นกฎหรือหากมันจะเปลี่ยนแปลงในอนาคตดังนั้นให้ใช้ความเสี่ยงของคุณเอง :)


48
ถ่ายทอดสดโดยคุณสมบัติที่ไม่มีเอกสารตายโดยคุณสมบัติที่ไม่มีเอกสาร ฉันชอบมัน!
Nate Cook

9
เราไม่ควรพึ่งพาhashValuesสิ่งเหล่านี้จริงๆ สิ่งที่เรารู้ก็คือมันมีค่าที่ไม่ซ้ำแบบสุ่ม - สามารถเปลี่ยนแปลงได้อย่างง่ายดายในอนาคตขึ้นอยู่กับรายละเอียดการใช้งานคอมไพเลอร์ แต่โดยรวมแล้วการขาดฟังก์ชั่นการนับในตัวรบกวน
Zorayr

16
หากคุณไม่ทราบการตั้งค่าอย่างชัดเจนcase ONE = 0แล้วคุณสามารถแทนที่ด้วยhashValue rawValue
Kevin Qi

3
ข้อกังวลที่นี่คือการใช้คุณสมบัติที่ไม่มีเอกสารของ hashValue ดังนั้นข้อเสนอแนะของฉันคือการใช้คุณสมบัติที่เป็นเอกสารของ rawValue
Kevin Qi

7
คุณได้ hardcoded ความจริงที่ว่าค่าคงที่เป็นค่าสูงสุดดีกว่าและปลอดภัยกว่าที่จะใช้สิ่งที่ต้องการstatic var count = 4แทนที่จะทิ้งชะตากรรมของคุณในชะตากรรมของการใช้งานในอนาคตของ Swift
Dale

72

ฉันกำหนดโปรโตคอลที่ใช้ซ้ำได้ซึ่งดำเนินการนับจำนวนเคสโดยอัตโนมัติตามวิธีการโพสต์โดย Nate Cook

protocol CaseCountable {
    static var caseCount: Int { get }
}

extension CaseCountable where Self: RawRepresentable, Self.RawValue == Int {
    internal static var caseCount: Int {
        var count = 0
        while let _ = Self(rawValue: count) {
            count += 1
        }
        return count
    }
}

จากนั้นฉันสามารถนำโปรโตคอลนี้กลับมาใช้ใหม่ได้เช่น:

enum Planet : Int, CaseCountable {
    case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
}
//..
print(Planet.caseCount)

1
ดีและสง่างามควรเป็นคำตอบที่ได้รับการยอมรับ IMHO
shannoga

1
อาจจะเป็นการดีกว่าที่จะเปลี่ยนcount++เป็นcount+=1เพราะ++โน้ตจะถูกลบใน Swift 3
Aladin

1
ทำแบบเดียวกันกับstatic var caseCount: Int { get }ไม่ได้เหรอ? ทำไมต้องมีstatic func?
pxpgraphics

ถ้าคุณพลาดคุณค่าใน Enum ล่ะ เช่นcase A=1, B=3?
Sasho

1
@Sasho แล้วมันจะไม่ทำงาน สิ่งนี้ต้องการให้กรณีของคุณเริ่มต้น0และไม่มีช่องว่าง
NRitH

35

สร้างอาร์เรย์ค่าคงที่ตามที่แสดงในคำตอบนี้

enum ProductCategory : String {
     case Washers = "washers", Dryers = "dryers", Toasters = "toasters"

     static let allValues = [Washers, Dryers, Toasters]
}

...

let count = ProductCategory.allValues.count

สิ่งนี้จะมีประโยชน์เมื่อคุณต้องการระบุค่าและใช้ได้กับ Enum ทุกประเภท


แม้ว่าจะไม่ได้สง่างามเท่าโซลูชันส่วนขยายและคู่มือมาก ๆ แต่ฉันเชื่อว่านี่เป็นประโยชน์มากที่สุดเนื่องจากมีจำนวนมากกว่า มันให้ลำดับของค่าและรายการค่าทั้งหมด
Nader Eloshaiker

2
นอกจากนี้คุณยังสามารถเพิ่มการนับเพื่อ enum static let count = allValues.countด้วยการทำ จากนั้นคุณสามารถทำให้เป็นallValuesส่วนตัวถ้าต้องการ
ThomasW

15

หากการนำไปปฏิบัติไม่มีอะไรเทียบกับการใช้จำนวนเต็มคุณสามารถเพิ่มค่าสมาชิกพิเศษที่เรียกว่าCountแทนจำนวนสมาชิกใน enum - ดูตัวอย่างด้านล่าง:

enum TableViewSections : Int {
  case Watchlist
  case AddButton
  case Count
}

ตอนนี้คุณสามารถรับจำนวนสมาชิกใน enum ได้ด้วยการโทร TableViewSections.Count.rawValueซึ่งจะส่งคืน 2 ตามตัวอย่างด้านบน

เมื่อคุณจัดการกับ enum ในคำสั่ง switch ตรวจสอบให้แน่ใจว่าได้ทิ้งความล้มเหลวในการยืนยันเมื่อพบCountสมาชิกที่คุณไม่คาดหวัง:

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  let currentSection: TableViewSections = TableViewSections.init(rawValue:section)!
  switch(currentSection) {
  case .Watchlist:
    return watchlist.count
  case .AddButton:
    return 1
  case .Count:
    assert(false, "Invalid table view section!")
  }
}

ฉันชอบโซลูชันนั้นเนื่องจากเปลี่ยนจำนวนอัตโนมัติเมื่อเพิ่มค่า enum เพิ่มเติม แต่เก็บไว้ในใจที่ว่านี้จะทำงานเฉพาะเมื่อ rawValues ของ enum ที่เริ่มต้นด้วย 0.
Joern

2
เห็นด้วยมีข้อ จำกัด สองประการ: ต้องเป็นจำนวนเต็มและต้องเริ่มต้นที่ศูนย์และดำเนินการเพิ่มขึ้นเรื่อย ๆ
Zorayr

3
ฉันคิดว่าจุดทั้งหมดของ Enum ที่มีพลังยิ่งกว่าของ Swift คือเราไม่ต้องใช้แฮ็คเดียวกันกับที่เราใช้ใน Objective-C: /
pkamb

14

ฟังก์ชันประเภทนี้สามารถคืนค่าจำนวนของ enum ของคุณได้

สวิฟท์ 2 :

func enumCount<T: Hashable>(_: T.Type) -> Int {
    var i = 1
    while (withUnsafePointer(&i) { UnsafePointer<T>($0).memory }).hashValue != 0 {
        i += 1
    }
    return i
}

สวิฟท์ 3 :

func enumCount<T: Hashable>(_: T.Type) -> Int {
   var i = 1
   while (withUnsafePointer(to: &i, {
      return $0.withMemoryRebound(to: T.self, capacity: 1, { return $0.pointee })
   }).hashValue != 0) {
      i += 1
   }
      return i
   }

3
ใช้งานไม่ได้กับ Swift 3 อีกต่อไปพยายามหาวิธีการนำไปใช้อย่างเหมาะสม แต่จะทำให้ว่างเปล่า
Cody Winton

นี้จะ unfun มากที่จะแก้ปัญหาถ้าอยู่หน่วยความจำทันทีที่อยู่ติดกับปลายenumเป็นยัง Hashableประเภทเดียวกัน
NRitH

10

String Enum พร้อมดัชนี

enum eEventTabType : String {
    case Search     = "SEARCH"
    case Inbox      = "INBOX"
    case Accepted   = "ACCEPTED"
    case Saved      = "SAVED"
    case Declined   = "DECLINED"
    case Organized  = "ORGANIZED"

    static let allValues = [Search, Inbox, Accepted, Saved, Declined, Organized]
    var index : Int {
       return eEventTabType.allValues.indexOf(self)!
    }
}

นับ: eEventTabType.allValues.count

ดัชนี: objeEventTabType.index

สนุก :)


10

โอ้เฮ้ทุกคนสิ่งที่เกี่ยวกับการทดสอบหน่วย?

func testEnumCountIsEqualToNumberOfItemsInEnum() {

    var max: Int = 0
    while let _ = Test(rawValue: max) { max += 1 }

    XCTAssert(max == Test.count)
}

สิ่งนี้รวมกับโซลูชันของ Antonio:

enum Test {

    case one
    case two
    case three
    case four

    static var count: Int { return Test.four.hashValue + 1}
}

ในรหัสหลักช่วยให้คุณO (1)บวกคุณจะได้รับการทดสอบล้มเหลวถ้ามีคนเพิ่มกรณี enum และไม่ปรับปรุงการดำเนินงานของfivecount


7

ฟังก์ชั่นนี้ขึ้นอยู่กับพฤติกรรมปัจจุบัน (Swift 1.1) ที่ไม่มีเอกสาร 2 รายการenum:

  • รูปแบบของหน่วยความจำเป็นเพียงดัชนีของenum caseถ้านับเป็นกรณีที่ 2-256 UInt8ก็
  • ถ้าenumบิตคาสต์จากดัชนีเคสไม่ถูกต้องค่านั้นhashValueคือ0

ใช้ความเสี่ยงของคุณเอง :)

func enumCaseCount<T:Hashable>(t:T.Type) -> Int {
    switch sizeof(t) {
    case 0:
        return 1
    case 1:
        for i in 2..<256 {
            if unsafeBitCast(UInt8(i), t).hashValue == 0 {
                return i
            }
        }
        return 256
    case 2:
        for i in 257..<65536 {
            if unsafeBitCast(UInt16(i), t).hashValue == 0 {
                return i
            }
        }
        return 65536
    default:
        fatalError("too many")
    }
}

การใช้งาน:

enum Foo:String {
    case C000 = "foo"
    case C001 = "bar"
    case C002 = "baz"
}
enumCaseCount(Foo) // -> 3

ในแอปพลิเคชั่นและรีดิชั่นจะ CRASH
HotJard

สิ่งนี้สามารถทำงานได้ในอุปกรณ์จำลอง แต่ไม่สามารถทำงานได้บนอุปกรณ์ 64 บิตจริง
Daniel Nord

5

ฉันเขียนส่วนขยายอย่างง่ายซึ่งให้ enums ทั้งหมดที่ค่าดิบเป็นจำนวนเต็มcountคุณสมบัติ:

extension RawRepresentable where RawValue: IntegerType {
    static var count: Int {
        var i: RawValue = 0
        while let _ = Self(rawValue: i) {
            i = i.successor()
        }
        return Int(i.toIntMax())
    }
}

น่าเสียดายที่มันให้countคุณสมบัติแก่OptionSetTypeที่ซึ่งมันทำงานไม่ถูกต้องดังนั้นนี่เป็นอีกเวอร์ชั่นที่ต้องมีความสอดคล้องกับCaseCountableโปรโตคอลสำหรับ enum ใด ๆ ที่คุณต้องการนับ:

protocol CaseCountable: RawRepresentable {}
extension CaseCountable where RawValue: IntegerType {
    static var count: Int {
        var i: RawValue = 0
        while let _ = Self(rawValue: i) {
            i = i.successor()
        }
        return Int(i.toIntMax())
    }
}

มันคล้ายกับวิธีที่โพสต์โดย Tom Pelaia แต่ใช้ได้กับทุกประเภทจำนวนเต็ม


4

แน่นอนมันไม่ได้เป็นแบบไดนามิก แต่สำหรับการใช้งานหลายอย่างคุณสามารถรับได้โดยเพิ่ม var แบบคงที่ใน Enum ของคุณ

static var count: Int{ return 7 }

แล้วใช้มันเป็น EnumName.count


3
enum EnumNameType: Int {
    case first
    case second
    case third

    static var count: Int { return EnumNameType.third.rawValue + 1 }
}

print(EnumNameType.count) //3

หรือ

enum EnumNameType: Int {
    case first
    case second
    case third
    case count
}

print(EnumNameType.count.rawValue) //3

* บน Swift 4.2 (Xcode 10) สามารถใช้:

enum EnumNameType: CaseIterable {
    case first
    case second
    case third
}

print(EnumNameType.allCases.count) //3

2

สำหรับกรณีการใช้งานของฉันใน codebase ที่หลายคนสามารถเพิ่มคีย์ใน enum และกรณีเหล่านี้ควรมีอยู่ในคุณสมบัติ allKeys เป็นสิ่งสำคัญที่ allKeys จะได้รับการตรวจสอบความถูกต้องกับคีย์ใน enum นี่คือเพื่อหลีกเลี่ยงบางคนลืมที่จะเพิ่มคีย์ของพวกเขาไปยังรายการคีย์ทั้งหมดการจับคู่การนับของอาร์เรย์ allKeys (สร้างขึ้นครั้งแรกเป็นชุดเพื่อหลีกเลี่ยงการซ้ำซ้อน) กับจำนวนของปุ่มใน enum ทำให้แน่ใจได้ว่ามีอยู่ทั้งหมด

บางคำตอบข้างต้นแสดงให้เห็นวิธีการที่จะบรรลุเป้าหมายนี้ในสวิฟท์ 2 แต่การทำงานไม่มีในสวิฟท์ 3 นี่คือเวอร์ชั่นที่จัดรูปแบบSwift 3 :

static func enumCount<T: Hashable>(_ t: T.Type) -> Int {
    var i = 1
    while (withUnsafePointer(to: &i) {
      $0.withMemoryRebound(to:t.self, capacity:1) { $0.pointee.hashValue != 0 }
    }) {
      i += 1
    }
    return i
}

static var allKeys: [YourEnumTypeHere] {
    var enumSize = enumCount(YourEnumTypeHere.self)

    let keys: Set<YourEnumTypeHere> = [.all, .your, .cases, .here]
    guard keys.count == enumSize else {
       fatalError("Missmatch between allKeys(\(keys.count)) and actual keys(\(enumSize)) in enum.")
    }
    return Array(keys)
}

ขึ้นอยู่กับกรณีการใช้งานของคุณคุณอาจต้องการเรียกใช้การทดสอบการพัฒนาเพื่อหลีกเลี่ยงค่าใช้จ่ายทั้งหมดที่ใช้ในแต่ละคำขอ


2

ทำไมคุณถึงทำให้มันซับซ้อนทั้งหมด ตัวนับ SIMPLEST ของ Int enum คือการเพิ่ม:

case Count

ในที่สุด และ ... วิโอลา - ตอนนี้คุณมีจำนวน - รวดเร็วและง่าย


1
a) เพิ่มกรณี enum ที่ไม่เกี่ยวข้องและ b) จะไม่ทำงานหากประเภทดิบของ Enum เป็นอย่างอื่นที่ไม่ใช่ Int
Robert Atkins

จริงๆแล้วนี่ไม่ใช่คำตอบที่ไม่ดี เช่นเดียวกับคำตอบของ @Tom Pelaia ข้างต้นอย่างไรก็ตามมันต้องใช้ค่า raw เพื่อเริ่มต้น0และไม่มีช่องว่างในลำดับ
NRitH

1

หากคุณไม่ต้องการให้โค้ดของคุณอยู่ใน enum สุดท้ายคุณสามารถสร้างฟังก์ชั่นนี้ภายใน enum ของคุณได้

func getNumberOfItems() -> Int {
    var i:Int = 0
    var exit:Bool = false
    while !exit {
        if let menuIndex = MenuIndex(rawValue: i) {
            i++
        }else{
            exit = true
        }
    }
    return i
}

1

สวิฟท์ 3รุ่นการทำงานกับIntประเภท enums:

protocol CaseCountable: RawRepresentable {}
extension CaseCountable where RawValue == Int {
    static var count: RawValue {
        var i: RawValue = 0
        while let _ = Self(rawValue: i) { i += 1 }
        return i
    }
}

เครดิต: ขึ้นอยู่กับคำตอบโดย bzz และ Nate Cook

ไม่รองรับ Generic IntegerType(ใน Swift 3 ที่เปลี่ยนชื่อเป็นInteger) เนื่องจากเป็นประเภททั่วไปที่แยกส่วนอย่างหนักซึ่งขาดฟังก์ชั่นมากมาย successorไม่สามารถใช้งานกับ Swift 3 ได้อีกต่อไป

โปรดทราบว่าความคิดเห็นจากคำสั่ง Code Commander ถึง Nate Cooks ยังคงใช้ได้:

ในขณะที่ดีเพราะคุณไม่จำเป็นต้อง hardcode ค่านี้จะยกตัวอย่างทุกค่า enum ทุกครั้งที่มีการเรียก นั่นคือ O (n) แทน O (1)

เท่าที่ฉันรู้ว่าขณะนี้ไม่มีวิธีแก้ไขเมื่อใช้สิ่งนี้เป็นส่วนขยายโพรโทคอล (และไม่นำไปใช้ในแต่ละ enum เช่น Nate Cook ได้) เนื่องจากคุณสมบัติที่จัดเก็บแบบคงที่ไม่ได้รับการสนับสนุนในประเภททั่วไป

อย่างไรก็ตามสำหรับ enums เล็ก ๆ นี้จะไม่มีปัญหา กรณีการใช้งานทั่วไปจะเป็นsection.countสำหรับการUITableViewsดังกล่าวแล้วโดย Zorayr


1

คำตอบของ Matthieu Riegler ที่ขยายออกไปนี้เป็นคำตอบสำหรับSwift 3ที่ไม่ต้องใช้ยาสามัญและสามารถเรียกได้ง่ายว่าใช้ชนิด enum ด้วยEnumType.elementsCount:

extension RawRepresentable where Self: Hashable {

    // Returns the number of elements in a RawRepresentable data structure
    static var elementsCount: Int {
        var i = 1
        while (withUnsafePointer(to: &i, {
            return $0.withMemoryRebound(to: self, capacity: 1, { return 
                   $0.pointee })
        }).hashValue != 0) {
            i += 1
        }
        return i
}

0

ฉันแก้ไขปัญหานี้ด้วยตนเองโดยการสร้างโปรโตคอล (EnumIntArray) และฟังก์ชั่นยูทิลิตี้ระดับโลก (enumIntArray) ที่ทำให้ง่ายต่อการเพิ่มตัวแปร "All" ให้กับ enum ใด ๆ (โดยใช้ swift 1.2) ตัวแปร "all" จะมีอาร์เรย์ขององค์ประกอบทั้งหมดใน enum เพื่อให้คุณสามารถใช้ all.count สำหรับการนับ

ใช้งานได้เฉพาะกับ enums ที่ใช้ค่า raw ประเภท Int แต่อาจให้แรงบันดาลใจบางอย่างสำหรับประเภทอื่น ๆ

นอกจากนี้ยังแก้ไขปัญหา "ช่องว่างในการกำหนดหมายเลข" และ "เวลามากเกินไปในการทำซ้ำ" ที่ฉันได้อ่านด้านบนและที่อื่น ๆ

แนวคิดคือการเพิ่มโปรโตคอล EnumIntArray ให้กับ enum ของคุณแล้วกำหนดตัวแปร "ทั้งหมด" แบบคงที่โดยการเรียกใช้ฟังก์ชัน enumIntArray และให้องค์ประกอบแรก (และสุดท้ายหากมีช่องว่างในการกำหนดหมายเลข)

เนื่องจากตัวแปรคงที่จะเริ่มต้นได้เพียงครั้งเดียวค่าโสหุ้ยในการผ่านค่าดิบทั้งหมดจะเข้าสู่โปรแกรมของคุณเพียงครั้งเดียว

ตัวอย่าง (ไม่มีช่องว่าง):

enum Animals:Int, EnumIntArray
{ 
  case Cat=1, Dog, Rabbit, Chicken, Cow
  static var all = enumIntArray(Animals.Cat)
}

ตัวอย่าง (กับช่องว่าง):

enum Animals:Int, EnumIntArray
{ 
  case Cat    = 1,  Dog, 
  case Rabbit = 10, Chicken, Cow
  static var all = enumIntArray(Animals.Cat, Animals.Cow)
}

นี่คือรหัสที่ใช้มัน:

protocol EnumIntArray
{
   init?(rawValue:Int)
   var rawValue:Int { get }
}

func enumIntArray<T:EnumIntArray>(firstValue:T, _ lastValue:T? = nil) -> [T]
{
   var result:[T] = []
   var rawValue   = firstValue.rawValue
   while true
   { 
     if let enumValue = T(rawValue:rawValue++) 
     { result.append(enumValue) }
     else if lastValue == nil                     
     { break }

     if lastValue != nil
     && rawValue  >  lastValue!.rawValue          
     { break }
   } 
   return result   
}

0

หรือคุณสามารถกำหนด_countค่า enum ภายนอกและแนบแบบคงที่:

let _count: Int = {
    var max: Int = 0
    while let _ = EnumName(rawValue: max) { max += 1 }
    return max
}()

enum EnumName: Int {
    case val0 = 0
    case val1
    static let count = _count
}

ด้วยวิธีนี้ไม่ว่าคุณจะสร้างขึ้นมาจำนวนเท่าใดมันจะถูกสร้างเพียงครั้งเดียว

(ลบคำตอบนี้ถ้าstaticไม่ได้)


0

วิธีการต่อไปนี้มาจากCoreKitและคล้ายกับคำตอบที่คนอื่น ๆ แนะนำ ใช้งานได้กับ Swift 4

public protocol EnumCollection: Hashable {
    static func cases() -> AnySequence<Self>
    static var allValues: [Self] { get }
}

public extension EnumCollection {

    public static func cases() -> AnySequence<Self> {
        return AnySequence { () -> AnyIterator<Self> in
            var raw = 0
            return AnyIterator {
                let current: Self = withUnsafePointer(to: &raw) { $0.withMemoryRebound(to: self, capacity: 1) { $0.pointee } }
                guard current.hashValue == raw else {
                    return nil
                }
                raw += 1
                return current
            }
        }
    }

    public static var allValues: [Self] {
        return Array(self.cases())
    }
}

enum Weekdays: String, EnumCollection {
    case sunday, monday, tuesday, wednesday, thursday, friday, saturday
}

Weekdays.allValues.countแล้วคุณก็ต้องการเพียงแค่โทร


0
enum WeekDays : String , CaseIterable
{
  case monday = "Mon"
  case tuesday = "Tue"
  case wednesday = "Wed"
  case thursday = "Thu"
  case friday = "Fri"
  case saturday = "Sat"
  case sunday = "Sun"
}

var weekdays = WeekDays.AllCases()

print("\(weekdays.count)")

-1
struct HashableSequence<T: Hashable>: SequenceType {
    func generate() -> AnyGenerator<T> {
        var i = 0
        return AnyGenerator {
            let next = withUnsafePointer(&i) { UnsafePointer<T>($0).memory }
            if next.hashValue == i {
                i += 1
                return next
            }
            return nil
        }
    }
}

extension Hashable {
    static func enumCases() -> Array<Self> {
        return Array(HashableSequence())
    }

    static var enumCount: Int {
        return enumCases().enumCount
    }
}

enum E {
    case A
    case B
    case C
}

E.enumCases() // [A, B, C]
E.enumCount   //  3

แต่ควรระมัดระวังในการใช้งานในประเภทที่ไม่ใช่ enum วิธีแก้ปัญหาบางอย่างอาจเป็น:

struct HashableSequence<T: Hashable>: SequenceType {
    func generate() -> AnyGenerator<T> {
        var i = 0
        return AnyGenerator {
            guard sizeof(T) == 1 else {
                return nil
            }
            let next = withUnsafePointer(&i) { UnsafePointer<T>($0).memory }
            if next.hashValue == i {
                i += 1
                return next
            }

            return nil
        }
    }
}

extension Hashable {
    static func enumCases() -> Array<Self> {
        return Array(HashableSequence())
    }

    static var enumCount: Int {
        return enumCases().count
    }
}

enum E {
    case A
    case B
    case C
}

Bool.enumCases()   // [false, true]
Bool.enumCount     // 2
String.enumCases() // []
String.enumCount   // 0
Int.enumCases()    // []
Int.enumCount      // 0
E.enumCases()      // [A, B, C]
E.enumCount        // 4

-1

มันสามารถใช้ค่าคงที่คงที่ซึ่งมีค่าสุดท้ายของการแจงนับบวกหนึ่ง

enum Color : Int {
    case  Red, Orange, Yellow, Green, Cyan, Blue, Purple

    static let count: Int = Color.Purple.rawValue + 1

    func toUIColor() -> UIColor{
        switch self {
            case .Red:
                return UIColor.redColor()
            case .Orange:
                return UIColor.orangeColor()
            case .Yellow:
                return UIColor.yellowColor()
            case .Green:
                return UIColor.greenColor()
            case .Cyan:
                return UIColor.cyanColor()
            case .Blue:
                return UIColor.blueColor()
            case .Purple:
                return UIColor.redColor()
        }
    }
}

-3

นี่เป็นเรื่องเล็กน้อย แต่ฉันคิดว่าทางออกที่ดีกว่า O (1) จะเป็นดังต่อไปนี้ ( เฉพาะเมื่อ enum ของคุณIntเริ่มต้นที่ x และอื่น ๆ ):

enum Test : Int {
    case ONE = 1
    case TWO
    case THREE
    case FOUR // if you later need to add additional enums add above COUNT so COUNT is always the last enum value 
    case COUNT

    static var count: Int { return Test.COUNT.rawValue } // note if your enum starts at 0, some other number, etc. you'll need to add on to the raw value the differential 
}

คำตอบที่เลือกในปัจจุบันที่ฉันยังเชื่อว่าเป็นคำตอบที่ดีที่สุดสำหรับ enums ทั้งหมดยกเว้นว่าคุณกำลังใช้งานอยู่Intฉันขอแนะนำวิธีแก้ปัญหานี้


3
การเพิ่มค่าให้กับ enum ของคุณที่ไม่ได้แสดงถึงประเภทของ enum นั้นเป็นกลิ่นที่ไม่ดี ฉันพบว่ามันยากที่จะแสดงเหตุผลรวมถึง "ALL" หรือ "NONE" แม้ว่าบางครั้งมันอาจดึงดูด การรวม "COUNT" ไว้เพียงเพื่อตัดปัญหานี้ก็เหม็นมาก
Michael Peterson

1
เหม็น? หากคุณต้องการที่จะเรียกมันว่าแน่นอน performant? ใช่. มันขึ้นอยู่กับนักพัฒนาในการตัดสินใจข้อดีข้อเสีย จริงๆแล้วนี่เป็นคำตอบเดียวกันกับคำตอบของ Zorayr ด้านบนที่เขาเข้าไปดูรายละเอียดเพิ่มเติมเกี่ยวกับมันและคำตอบที่ยอมรับใหม่ก็คล้ายกัน แต่จนกระทั่งรวดเร็วเพิ่ม API สำหรับมัน นี่คือสิ่งที่พวกเราบางคนตัดสินใจใช้ คุณสามารถเพิ่มฟังก์ชั่นที่ตรวจสอบความถูกต้องของค่า enum ที่guardต่อต้านCOUNTและส่งข้อผิดพลาดคืนค่าเท็จ ฯลฯ เพื่อแก้ไขข้อกังวลของคุณเกี่ยวกับการเป็นตัวแทนของประเภท
Drmorgan
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.