Memeriksa nilai Bool Opsional

89

Saat saya ingin memeriksa apakah Opsional Bool benar, melakukan ini tidak berhasil:

var boolean : Bool? = false
if boolean{
}

Ini menghasilkan kesalahan ini:

Jenis opsional '@IvalueBool?' tidak dapat digunakan sebagai boolean; uji untuk '! = nil' sebagai gantinya

Saya tidak ingin memeriksa nol; Saya ingin memeriksa apakah nilai yang dikembalikan benar.

Apakah saya selalu harus melakukannya if boolean == truejika saya bekerja dengan Opsional Bool?

Karena Opsional tidak sesuai BooleanTypelagi, bukankah kompiler harus tahu bahwa saya ingin memeriksa nilai Bool?

Kucing Bulan
sumber
Karena Boolean sesuai dengan protokol Equatable, maka Anda dapat membandingkan opsional dengan non-opsional. Lihat di sini
Madu

Jawaban:

197

Dengan boolean opsional, diperlukan pemeriksaan yang eksplisit:

if boolean == true {
    ...
}

Jika tidak, Anda dapat membuka bungkus opsional:

if boolean! {
    ...
}

Tapi itu menghasilkan pengecualian waktu proses jika boolean adalah nil- untuk mencegahnya:

if boolean != nil && boolean! {
    ...
}

Sebelum beta 5 itu mungkin, tetapi itu telah diubah seperti yang dilaporkan dalam catatan rilis:

Opsional tidak lagi secara implisit mengevaluasi ke benar saat memiliki nilai dan salah jika tidak, untuk menghindari kebingungan saat bekerja dengan nilai Bool opsional. Sebaliknya, buat pemeriksaan eksplisit terhadap nil dengan operator == atau! = Untuk mengetahui apakah sebuah opsional berisi nilai.

Tambahan: seperti yang disarankan oleh @MartinR, variasi yang lebih ringkas untuk opsi ke-3 menggunakan operator penggabungan:

if boolean ?? false {
    // this code runs only if boolean == true
}

yang berarti: jika boolean bukan nil, ekspresi mengevaluasi ke nilai boolean (yaitu menggunakan nilai boolean yang tidak dibungkus), jika tidak ekspresi mengevaluasi ke false

Antonio
sumber
4
Pilihan ketiga adalah solusi yang disukai karena ini adalah cara terbaik untuk mengungkapkan maksud kode. Menggunakan if letjuga akan berhasil.
Sulthan
29
Sebuah varian dari pilihan ketiga, menggunakan "operator penggabungan nihil ??": if boolean ?? false { ... } .
Martin R
4
Tetapi jika saya ingin meniadakannya, itu mulai terlihat konyol: if !(boolean ?? true) { ... }:(
Andreas
2
Membuka paksa ide yang sangat buruk. Itu harus selalu dihindari.
Matthieu Riegler
5
Apa yang salah dengan opsi pertama? Bagi saya ini sepertinya cara terbaik.
Vahid Amiri
43

Penjilidan opsional

Cepat 3 & 4

var booleanValue : Bool? = false
if let booleanValue = booleanValue, booleanValue {
    // Executes when booleanValue is not nil and true
    // A new constant "booleanValue: Bool" is defined and set
    print("bound booleanValue: '\(booleanValue)'")
}

Swift 2.2.0

var booleanValue : Bool? = false
if let booleanValue = booleanValue where booleanValue {
    // Executes when booleanValue is not nil and true
    // A new constant "booleanValue: Bool" is defined and set
    print("bound booleanValue: '\(booleanValue)'")
}

Kode let booleanValue = booleanValuekembali falsejika booleanValueada nildan ifblok tidak dijalankan. Jika booleanValuetidak nil, kode ini mendefinisikan variabel baru bernama booleanValuetipe Bool(bukan opsional, Bool?).

Kode Swift 3 & 4 booleanValue(dan kode Swift 2.2 where booleanValue) mengevaluasi booleanValue: Boolvariabel baru . Jika benar, ifblok dijalankan dengan booleanValue: Boolvariabel yang baru didefinisikan dalam cakupan (memungkinkan opsi untuk mereferensikan nilai terikat lagi di dalam ifblok).

Catatan: Ini adalah konvensi Swift untuk menamai konstanta / variabel terikat sama dengan konstanta / variabel opsional seperti let booleanValue = booleanValue. Teknik ini disebut variabel shadowing . Anda bisa keluar dari konvensi dan menggunakan sesuatu seperti let unwrappedBooleanValue = booleanValue, unwrappedBooleanValue. Saya menunjukkan ini untuk membantu memahami apa yang terjadi. Saya merekomendasikan menggunakan variabel shadowing.

 

Pendekatan Lainnya

Penggabungan nol

Penggabungan nol jelas untuk kasus khusus ini

var booleanValue : Bool? = false
if booleanValue ?? false {
    // executes when booleanValue is true
    print("optional booleanValue: '\(booleanValue)'")
}

Memeriksa falsetidak begitu jelas

var booleanValue : Bool? = false
if !(booleanValue ?? false) {
    // executes when booleanValue is false
    print("optional booleanValue: '\(booleanValue)'")
}

Catatan: if !booleanValue ?? falsetidak dapat dikompilasi.

 

Paksa buka bungkus opsional (hindari)

Buka paksa bungkusnya meningkatkan kemungkinan bahwa seseorang akan membuat perubahan di masa mendatang yang terkompilasi tetapi mengalami error saat runtime. Oleh karena itu, saya akan menghindari hal seperti ini:

var booleanValue : Bool? = false
if booleanValue != nil && booleanValue! {
    // executes when booleanValue is true
    print("optional booleanValue: '\(booleanValue)'")
}

 

Pendekatan Umum

Meskipun pertanyaan stack overflow ini menanyakan secara spesifik bagaimana cara memeriksa apakah a Bool?ada truedalam ifpernyataan, akan sangat membantu untuk mengidentifikasi pendekatan umum apakah memeriksa true, false, atau menggabungkan nilai yang tidak dibungkus dengan ekspresi lain.

Karena ungkapan menjadi lebih rumit, saya menemukan pendekatan penjilidan opsional lebih fleksibel dan lebih mudah dipahami daripada pendekatan lain. Perhatikan bahwa karya mengikat opsional dengan jenis opsional ( Int?, String?, dll).

Ponsel Dan
sumber
Saya mengalami kesulitan menggunakan ekspresi Boolean dengan loop opsional for while. Operator penggabungan nol berfungsi, tetapi ini berantakan dan rawan kesalahan. Apakah ada cara untuk menggunakannya if let?
jbaraga
@jbaraga, kirimkan contoh while loop yang ingin Anda ketahui.
Seluler Dan
Dalam menggunakan array sebagai tumpukan, saya ingin memunculkan nilai sampai kondisi terpenuhi, atau tumpukan kosong. Misalnya,while array.last < threshold { array.removeLast() }
jbaraga
Anda dapat menyelesaikan pemrosesan tumpukan itu dengan if, let, wheremenggunakan ini: while let last = array.last where last < threshold { array.removeLast() }di Swift 2 atau while let last = array.last, last < threshold { array.removeLast() }di Swift 3.
Seluler Dan
Itu lebih baik, terima kasih. Saya tidak menyadarinya while let.
jbaraga
2
var enabled: Bool? = true

if let enabled = enabled, enabled == true {
    print("when is defined and true at the same moment")
}

if enabled ?? false {
    print("when is defined and true at the same moment")
}

if enabled == .some(true) {
    print("when is defined and true at the same moment")
}

if enabled == (true) {
    print("when is defined and true at the same moment")
}

if case .some(true) = enabled {
    print("when is defined and true at the same moment")
}

if enabled == .some(false) {
    print("when is defined and false at the same moment")
}

if enabled == (false) {
    print("when is defined and false at the same moment")
}

if enabled == .none {
    print("when is not defined")
}

if enabled == nil {
    print("when is not defined")
}
Blazej SLEBODA
sumber
0

Saya menemukan solusi lain, membebani operator Boolean. Sebagai contoh:

public func < <T: Comparable> (left: T?, right: T) -> Bool {
    if let left = left {
        return left < right
    }
    return false
}

Ini mungkin tidak sepenuhnya dalam "semangat" perubahan bahasa, tetapi memungkinkan untuk membuka bungkus opsional dengan aman, dan dapat digunakan untuk kondisi di mana saja, termasuk while loop.

jbaraga
sumber
1
Maaf, melihat kembali posting asli, itu tidak menjawab pertanyaan spesifik itu, melainkan pertanyaan yang saya ajukan dalam komentar saya sebelumnya.
jbaraga
Saya akan sangat berhati-hati dalam menggunakan kelebihan beban ini, karena mungkin ada kasus di mana Anda tidak ingin nil diperlakukan sebagai "lebih besar dari" nilai bukan nol (Anda mungkin menginginkan hasil yang berlawanan dalam konteks tertentu, atau mungkin alternatif penanganan seluruhnya). Menggunakan pembongkaran normal malah memaksa Anda untuk secara eksplisit menangani bagaimana Anda ingin menangani nil dalam setiap kasus, sehingga Anda cenderung tidak mengalami hasil yang tidak terduga.
John Montgomery
0

Jawaban yang menurut saya paling mudah dibaca adalah dengan mendefinisikan suatu fungsi. Tidak terlalu rumit tetapi berhasil.

func isTrue(_ bool: Bool?) -> Bool {
    guard let b = bool else {
        return false
    }
    return b
}

pemakaian:

let b: Bool? = true
if isTrue(b) {
    // b exists and is true
} else {
    // b does either not exist or is false
}
Charel ctk
sumber
0

Seperti kata Antonio

Opsional tidak lagi secara implisit mengevaluasi ke benar saat memiliki nilai dan salah jika tidak, untuk menghindari kebingungan saat bekerja dengan nilai Bool opsional. Sebaliknya, buat pemeriksaan eksplisit terhadap nil dengan operator == atau! = Untuk mengetahui apakah sebuah opsional berisi nilai.

Saya menghabiskan beberapa jam mencoba memahami sebaris kode yang saya temukan, tetapi utas ini menempatkan saya di jalur yang benar.

Kutipan ini dari Agustus 2014 , dan sejak itu Apple memperkenalkan Neverberikut usulan SE-0102 dan yang terakhir dibuat sesuai dengan equatable, Hashable, Kesalahan dan Sebanding

Sekarang dimungkinkan untuk memeriksa apakah boolean nilmenggunakan Never?:


var boolean: Bool? = false
boolean is Never? // false
boolean = true
boolean is Never? // false
boolean = nil
boolean is Never? // true

Anda sebenarnya dapat menggunakan jenis lain yang tidak bisa dihuni :

public enum NeverEver { }
var boolean: Bool? = false
boolean is NeverEver? // false
boolean = true
boolean is NeverEver? // false
boolean = nil
boolean is NeverEver? // true

Karena itu, Anda juga dapat menggunakan pembungkus properti sekarang:

@propertyWrapper struct OptionalBool {
    public var wrappedValue: Bool?
    public var projectedValue: Bool { wrappedValue ?? false }
    public init(wrappedValue: Bool?) {
        self.wrappedValue = wrappedValue
    }
}

struct Struct {
    @OptionalBool var predicate: Bool?
    var description: String {
        if $predicate {
            return "predicate is true"
        }
        return "predicate is false"
    }
}

var object = Struct()
object.description // "predicate is false"
object.predicate = false
object.description // "predicate is false"
object.predicate = true
object.description // "predicate is true"

atau bahkan:

@propertyWrapper struct OptionalBool {
    var wrappedValue: Bool?
    var projectedValue: OptionalBool { self }
    var isNil: Bool { wrappedValue is Never? }
    var value: Bool { wrappedValue ?? false }
    
    init(wrappedValue: Bool?) {
        self.wrappedValue = wrappedValue
    }
}

struct Struct {
    @OptionalBool var predicate: Bool?
    var description: String {
        if $predicate.value {
            return "predicate is true"
        }
        if !$predicate.isNil {
            return "predicate is false"
        }
        return "predicate is nil"
    }
}

var object = Struct()
object.description // "predicate is nil"
object.predicate = false
object.description // "predicate is false"
object.predicate = true
object.description // "predicate is true"

AnderCover
sumber