Swift: menguji nilai opsional dalam kasus sakelar

94

Di Swift, bagaimana saya bisa menulis kasus dalam pernyataan switch yang menguji nilai yang dialihkan dengan konten opsional , melewati kasus jika opsional berisi nil?

Begini cara saya membayangkan ini mungkin terlihat:

let someValue = 5
let someOptional: Int? = nil

switch someValue {
case someOptional:
    // someOptional is non-nil, and someValue equals the unwrapped contents of someOptional
default:
    // either, someOptional is nil, or someOptional is non-nil but someValue does not equal the unwrapped contents of someOptional
}

Jika saya hanya menulisnya persis seperti ini, kompilator mengeluh bahwa someOptionaltidak terbungkus, tetapi jika saya secara eksplisit membukanya dengan menambahkan di !bagian akhir, saya tentu saja mendapatkan kesalahan runtime setiap saat someOptionalberisi nil. Menambahkan ?alih-alih !akan masuk akal bagi saya (dalam semangat rangkaian opsional, saya kira), tetapi tidak membuat kesalahan kompiler hilang (yaitu tidak benar-benar membuka bungkus opsional).

George WS
sumber

Jawaban:

113

Opsional adalah enumseperti ini:

enum Optional<T> : Reflectable, NilLiteralConvertible {
    case none
    case some(T)

    // ...
}

Jadi, Anda dapat mencocokkannya seperti biasa dengan pola pencocokan "Nilai Terkait" :

let someValue = 5
let someOptional: Int? = nil

switch someOptional {
case .some(someValue):
    println("the value is \(someValue)")
case .some(let val):
    println("the value is \(val)")
default:
    println("nil")
}

Jika Anda ingin cocok someValue, gunakan ekspresi penjaga :

switch someValue {
case let val where val == someOptional:
    println(someValue)
default:
    break
}

Dan untuk Swift> 2.0

switch someValue {
case let val where val == someOptional:
    print("matched")
default:
    print("didn't match; default")        
}
rintaro
sumber
5
Perhatikan bahwa di Swift 3, beberapa / tidak ada huruf kecil, yaitu Anda akan menggunakan .some daripada .Some
Adam
55

Mulai Xcode 7, " x?pola baru dapat digunakan untuk mencocokkan pola dengan pilihan sebagai sinonim untuk .some(x)". Ini berarti bahwa di Swift 2 dan yang lebih baru, variasi jawaban rintaro berikut ini juga akan berfungsi:

let knownValue = 5

switch someOptional {
case knownValue?:
    // Contents of someOptional are knownValue, defined above.
case let otherValue?:
    // Contents of someOptional are *any* non-nil value not already tested for.
    // Unwrapped contents are assigned to otherValue for use inside this case.
default:
    // someOptional is nil.
}
Slipp D. Thompson
sumber
3
Pertanyaannya adalah tentang mencocokkan nilai non-opsional dengan opsional, jawaban ini adalah sebaliknya.
Martin R
2
Benar, namun jawaban ini pada awalnya ditulis oleh OP sebagai pembaruan untuk pertanyaan tersebut sehingga baginya itu adalah solusi yang tidak dapat disangkal; Saya baru saja memindahkannya ke jawaban wiki komunitas. Mungkin @GeorgeWS dapat menjelaskan mengapa mengganti switch & case args berfungsi untuk use case-nya?
Slipp D. Thompson
2
Saya agak tersesat. apa perbedaan antara dua kasus pertama Anda? someValue?adalah beberapa nilai lain yang ditentukan, tetapi case let val?hanya versi aman yang tidak terbungkus someOptional?!
Madu
@ Madu Ini bukan contoh kode dunia nyata; itu hanyalah variasi dari jawaban rintaro. Jadi, tanyakan padanya pertanyaan itu— jawaban saya secara fungsional setara dengan kode dalam dirinya. Jika Anda bertanya rintaro, saya yakin jawabannya adalah 1. Ini mencerminkan apa yang ada di dokumen Apple yang ditautkan; 2. itu hanya menunjukkan sintaks; itu tidak mencapai perhitungan yang berbeda atau tujuan logika bisnis.
Slipp D. Thompson
@Honey Juga, jawaban rintaro awalnya ditulis untuk Swift 1.x dan diperbarui untuk Swift 2. Ada kemungkinan bahwa versi tanpa letkompilasi lagi. Saya tidak dapat mengingat sekarang mengapa hal itu berhasil di masa lalu.
Slipp D. Thompson
10

Di Swift 4 Anda dapat menggunakan Opsional: ExpressibleByNilLiteral dari Apple untuk membungkus opsional

https://developer.apple.com/documentation/swift/optional

Contoh

enum MyEnum {
    case normal
    case cool
}

beberapa

let myOptional: MyEnum? = MyEnum.normal

switch smyOptional {
    case .some(.normal): 
    // Found .normal enum
    break

    case .none: 
    break

    default:
    break
}

tidak ada

let myOptional: MyEnum? = nil

switch smyOptional {
    case .some(.normal): 
    break

    case .none: 
    // Found nil
    break

    default:
    break
}

default

let myOptional: MyEnum? = MyEnum.cool

switch smyOptional {
    case .some(.normal): 
    break

    case .none: 
    break

    default:
    // Found .Cool enum
    break
}

Enum dengan nilai

enum MyEnum {
    case normal(myValue: String)
    case cool
}

beberapa nilai

let myOptional: MyEnum? = MyEnum.normal("BlaBla")

switch smyOptional {
case .some(.normal(let myValue)) where myValue == "BlaBla":
    // Here because where find in my myValue "BlaBla"
    break

// Example for get value
case .some(.normal(let myValue)):
    break

// Example for just know if is normal case enum
case .some(.normal):
    break

case .none:
    break

default:

    break
}
YannSteph
sumber
Anda menyebutkan ExpressibleByNilLiteraltetapi kemudian itu tidak benar-benar digunakan dalam deklarasi enum dll. Apa itu?
pkamb