607 Stimmen

Wie nummeriert man einen Aufzählungstyp mit String-Typ?

enum Suit: String {
    case spades = ""
    case hearts = ""
    case diamonds = ""
    case clubs = ""
}

Zum Beispiel, wie kann ich so etwas machen:

for suit in Suit {
    // Bearbeite etwas mit dem Anzug
    print(suit.rawValue)
}

Ergebnisbeispiel:

540voto

rougeExciter Punkte 7377

Dieser Beitrag ist relevant hier https://www.swift-studies.com/blog/2014/6/10/enumerating-enums-in-swift

Im Wesentlichen besteht die vorgeschlagene Lösung aus

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

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

for category in ProductCategory.allValues{
     //Etwas tun
}

351voto

Cœur Punkte 34332

Swift 4.2+

Ab Swift 4.2 (mit Xcode 10) genügt es, dem CaseIterable-Protokoll zu entsprechen, um von allCases zu profitieren. Um diese Protokollkonformität hinzuzufügen, müssen Sie einfach irgendwo Folgendes schreiben:

extension Suit: CaseIterable {}

Wenn das Enum Ihr eigenes ist, können Sie die Konformität direkt in der Deklaration angeben:

enum Suit: String, CaseIterable { case spades = ""; case hearts = ""; case diamonds = ""; case clubs = "" }

Dann wird der folgende Code alle möglichen Werte ausgeben:

Suit.allCases.forEach {
    print($0.rawValue)
}

Kompatibilität mit früheren Swift-Versionen (3.x und 4.x)

Wenn Sie Swift 3.x oder 4.0 unterstützen müssen, können Sie die Implementierung von Swift 4.2 nachahmen, indem Sie den folgenden Code hinzufügen:

#if !swift(>=4.2)
public protocol CaseIterable {
    associatedtype AllCases: Collection where AllCases.Element == Self
    static var allCases: AllCases { get }
}
extension CaseIterable where Self: Hashable {
    static var allCases: [Self] {
        return [Self](AnySequence { () -> AnyIterator in
            var raw = 0
            var first: Self?
            return AnyIterator {
                let current = withUnsafeBytes(of: &raw) { $0.load(as: Self.self) }
                if raw == 0 {
                    first = current
                } else if current == first {
                    return nil
                }
                raw += 1
                return current
            }
        })
    }
}
#endif

291voto

rintaro Punkte 51571

Ich habe eine Hilfsfunktion iterateEnum() erstellt, um Fälle für beliebige enum-Typen zu durchlaufen.

Hier ist ein Beispiel zur Verwendung:

enum Suit: String {
    case Spades = ""
    case Hearts = ""
    case Diamonds = ""
    case Clubs = ""
}

for f in iterateEnum(Suit) {
    println(f.rawValue)
}

Das Ergebnis ist:

Dies dient jedoch nur zu Debug- oder Testzwecken: Dies basiert auf verschiedenen nicht dokumentierten Verhaltensweisen des Swift 1.1 Compilers, daher verwenden Sie es auf eigene Gefahr.

Hier ist der Code:

func iterateEnum(_: T.Type) -> GeneratorOf {
    var cast: (Int -> T)!
    switch sizeof(T) {
        case 0: return GeneratorOf(GeneratorOfOne(unsafeBitCast((), T.self)))
        case 1: cast = { unsafeBitCast(UInt8(truncatingBitPattern: $0), T.self) }
        case 2: cast = { unsafeBitCast(UInt16(truncatingBitPattern: $0), T.self) }
        case 4: cast = { unsafeBitCast(UInt32(truncatingBitPattern: $0), T.self) }
        case 8: cast = { unsafeBitCast(UInt64($0), T.self) }
        default: fatalError("kann nicht hier sein")
    }

    var i = 0
    return GeneratorOf {
        let next = cast(i)
        return next.hashValue == i++ ? next : nil
    }
}

Die zugrunde liegende Idee lautet:

  • Die Speicherrepräsentation eines enum, ausgenommen enums mit assoziierten Typen, ist nur ein Index von Fällen, wenn die Anzahl der Fälle 2...256 beträgt, ist sie identisch mit UInt8, wenn sie 257...65536 beträgt, ist sie UInt16 usw. Daher kann sie durch unsafeBitcast von entsprechenden unsigned integer Typen erfolgen.
  • .hashValue von Enum-Werten ist dasselbe wie der Index des Falles.
  • .hashValue von Enum-Werten, die aus einem ungültigen Index umgewandelt wurden, ist 0.

Überarbeitet für Swift2 und implementierte Casting-Ideen aus Antwort von @Kametrixom:

func iterateEnum(_: T.Type) -> AnyGenerator {
    var i = 0
    return anyGenerator {
        let next = withUnsafePointer(&i) { UnsafePointer($0).memory }
        return next.hashValue == i++ ? next : nil
    }
}

Überarbeitet für Swift3:

func iterateEnum(_: T.Type) -> AnyIterator {
    var i = 0
    return AnyIterator {
        let next = withUnsafePointer(to: &i) {
            $0.withMemoryRebound(to: T.self, capacity: 1) { $0.pointee }
        }
        if next.hashValue != i { return nil }
        i += 1
        return next
    }
}

Überarbeitet für Swift3.0.1:

func iterateEnum(_: T.Type) -> AnyIterator {
    var i = 0
    return AnyIterator {
        let next = withUnsafeBytes(of: &i) { $0.load(as: T.self) }
        if next.hashValue != i { return nil }
        i += 1
        return next
    }
}

135voto

sdduursma Punkte 2611

Die anderen Lösungen funktionieren, aber sie machen alle Annahmen zum Beispiel über die Anzahl der möglichen Ränge und Farben oder darüber, was der erste und letzte Rang sein könnte. Natürlich wird sich die Struktur eines Kartenspiels wahrscheinlich in absehbarer Zukunft nicht wesentlich ändern. Im Allgemeinen ist es jedoch sauberer, Code zu schreiben, der so wenig Annahmen wie möglich macht. Meine Lösung:

Ich habe einen Raw-Typ zum Suit enum hinzugefügt, damit ich Suit(rawValue:) verwenden kann, um auf die Suit Fälle zuzugreifen:

enum Suit: Int {
    case Spades = 1
    case Hearts, Diamonds, Clubs
    func simpleDescription() -> String {
        switch self {
            case .Spades:
                return "Pik"
            case .Hearts:
                return "Herz"
            case .Diamonds:
                return "Karo"
            case .Clubs:
                return "Kreuz"
        }
    }
    func color() -> String {
        switch self {
            case .Spades:
                return "schwarz"
            case .Clubs:
                return "schwarz"
            case .Diamonds:
                return "rot"
            case .Hearts:
                return "rot"
        }
    }
}

enum Rank: Int {
    case Ace = 1
    case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
    case Jack, Queen, King
    func simpleDescription() -> String {
        switch self {
            case .Ace:
                return "Ass"
            case .Jack:
                return "Bube"
            case .Queen:
                return "Dame"
            case .King:
                return "König"
            default:
                return String(self.rawValue)
        }
    }
}

Unten ist die Implementierung der Methode createDeck() für die Karte. init(rawValue:) ist ein Initialisierer mit Fallibilismus und gibt ein Optional zurück. Indem man den Wert in beiden while-Anweisungen auspackt und überprüft, ist es nicht notwendig, die Anzahl der Rank oder Suit Fälle anzunehmen:

struct Card {
    var rank: Rank
    var suit: Suit
    func simpleDescription() -> String {
        return "Die \(rank.simpleDescription()) von \(suit.simpleDescription())"
    }
    func createDeck() -> [Card] {
        var n = 1
        var deck = [Card]()
        while let rank = Rank(rawValue: n) {
            var m = 1
            while let suit = Suit(rawValue: m) {
                deck.append(Card(rank: rank, suit: suit))
                m += 1
            }
            n += 1
        }
        return deck
    }
}

So wird die Methode createDeck aufgerufen:

let card = Card(rank: Rank.Ace, suit: Suit.Clubs)
let deck = card.createDeck()

77voto

Kametrixom Punkte 14205

Ich habe in den Bits und Bytes gestolpert und eine Erweiterung erstellt, die sich später als sehr ähnlich zu der Antwort von @rintaro herausstellte. Es wird wie folgt verwendet:

enum E : EnumCollection {
    case A, B, C
}

Array(E.cases())    // [A, B, C]

Bemerkenswert ist, dass es auf jedem Enum ohne assoziierte Werte verwendet werden kann. Beachten Sie, dass dies nicht für Enums funktioniert, die keine Fälle haben.

Wie bei der Antwort von @rintaro verwendet dieser Code die zugrunde liegende Darstellung eines Enums. Diese Darstellung ist nicht dokumentiert und könnte sich in Zukunft ändern, was zu einem Fehler führen würde. Ich empfehle nicht die Verwendung davon in der Produktion.

Code (Swift 2.2, Xcode 7.3.1, funktioniert nicht auf Xcode 10):

protocol EnumCollection : Hashable {}
extension EnumCollection {
    static func cases() -> AnySequence {
        typealias S = Self
        return AnySequence { () -> AnyGenerator in
            var raw = 0
            return AnyGenerator {
                let current : Self = withUnsafePointer(&raw) { UnsafePointer($0).memory }
                guard current.hashValue == raw else { return nil }
                raw += 1
                return current
            }
        }
    }
}

~~

Code (Swift 3, Xcode 8.1, funktioniert nicht auf Xcode 10):

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

~~

Ich habe keine Ahnung, warum ich typealias benötige, aber der Compiler beschwert sich ohne es.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X