web-dev-qa-db-ja.com

引数を持つ列挙型でif-else比較を行う方法

言語:Swift2.3

たとえば、さまざまな種類の列挙型を紹介しましょう

enum Normal {
    case one
    case two, three
}

enum NormalRaw: Int {
    case one
    case two, three
}

enum NormalArg {
    case one(Int)
    case two, three
}   

Switchは、次のように3つの列挙すべてで使用できます。

var normal: Normal = .one
var normalRaw: NormalRaw = .one
var normalArg: NormalArg = .one(1)

switch normal {
    case .one: print("1")
    default: break
}

switch normalRaw {
    case .one: print(normalRaw.rawValue)
    default: break
}

switch normalArg {
    case .one(let value): print(value)
    default: break
}

If-elseステートメントでは、NormalNormalRawの比較しかできず、NormalArgのエラーメッセージが表示されるため、コードを実行できません。

二項演算子 '=='は、タイプNormalArgおよび_のオペランドには適用できません

コード例は次のとおりです。

if normal == .two { // no issue
    .. do something
}

if normalRaw == .two { // no issue
    .. do something
}

if normalArg == .two { // error here (the above message)
    .. do something
}

if normalArg == .one(_) { // error here (the above message)
    .. do something
}

if normalArg == .three { // error here (the above message)
    .. do something
}

何か案は?私はこのコードで実際に何もしていません。なぜ比較ができないのか疑問に思っています。

27
Zonily Jame

トリックは、==で実際にチェックするのではなく、ifステートメントで単一の=と組み合わせてcaseキーワードを使用することです。これは最初は直感的な小さなカウンターですが、if let、あなたはかなり早く慣れる:

enum Normal {
    case one
    case two, three
}

enum NormalRaw: Int {
    case one = 1
    case two, three
}

enum NormalArg {
    case one(Int)
    case two, three
}


let normalOne = Normal.one
let normalRawOne = NormalRaw.one
let normalArgOne = NormalArg.one(1)

if case .one = normalOne {
    print("A normal one") //prints "A normal one"
}

if case .one = normalRawOne {
    print("A normal \(normalRawOne.rawValue)") //prints "A normal 1"
}

if case .one(let value) = normalArgOne {
    print("A normal \(value)") //prints "A normal 1"
}

ポイントは、in Swiftでは、enumが生の型を使用する場合、または関連付けられた値がない場合にのみ、enumの方程式を無料で取得できます(試して、両方で使用することはできません同じ時間)Swiftただし、ケースを関連する値と比較する方法がわからない-どうすればできるのか?この例を見てみましょう:

Normal.one == .one //true
Normal.one == .two //false

NormalRaw.one == .one //true
NormalRaw.one == .two //false

NormalArg.one(1) == .one(1) //Well...?
NormalArg.one(2) == .one(1) //Well...?
NormalArg.one(1) == .two //Well...?

たぶん、これはなぜこれが箱から出して動作しないのかを明確にする:

class Special {
    var name: String?
    var special: Special?
}

enum SpecialEnum {
    case one(Special)
    case two
}

var special1 = Special()
special1.name = "Hello"

var special2 = Special()
special2.name = "World"
special2.special = special1

SpecialEnum.one(special1) == SpecialEnum.one(special2) //Well...?

したがって、関連付けられた値を持つ列挙型が必要な場合は、自分で列挙型にEquatableプロトコルを実装する必要があります。

enum NormalArg: Equatable {
    case one(Int)
    case two

    static func ==(lhs: NormalArg, rhs: NormalArg) -> Bool {
        switch (lhs, rhs) {
        case (let .one(a1), let .one(a2)):
            return a1 == a2
        case (.two,.two):
            return true
        default:
            return false
        }
    }
}
83
xxtesaxx

答えはEquatable Protocolです。

それでは、その仕組みを見てみましょう。

たとえば、次の列挙型を検討してください。

enum Barcode {
    case upca(Int, Int)
    case qrCode(String)
    case none
}

等値演算子==をenumでチェックすると、失敗します。

// Error: binary operator '==' cannot be applied to two Barcode operands
Barcode.qrCode("code") == Barcode.qrCode("code")

Equatable Protocolを使用してこれを修正する方法は?

extension Barcode: Equatable {
}

func ==(lhs: Barcode, rhs: Barcode) -> Bool {
    switch (lhs, rhs) {
    case (let .upca(codeA1, codeB1), let .upca(codeA2, codeB2)):
        return codeA1 == codeA2 && codeB1 == codeB2

    case (let .qrCode(code1), let .qrCode(code2)):
        return code1 == code2

    case (.None, .None):
        return true

    default:
        return false
    }
}

Barcode.qrCode("code") == Barcode.qrCode("code") // true
Barcode.upca(1234, 1234) == Barcode.upca(4567, 7890) // false
Barcode.none == Barcode.none // true
7
aashish tamsya