การตรวจสอบค่าของบูลเสริม


89

เมื่อฉันต้องการตรวจสอบว่าบูลเสริมเป็นจริงการทำเช่นนี้ไม่ได้ผล:

var boolean : Bool? = false
if boolean{
}

ส่งผลให้เกิดข้อผิดพลาดนี้:

ประเภทตัวเลือก "@IvalueBool?" ไม่สามารถใช้เป็นบูลีนได้ ทดสอบ '! = nil' แทน

ฉันไม่ต้องการตรวจสอบศูนย์ ฉันต้องการตรวจสอบว่าค่าที่ส่งคืนเป็นจริงหรือไม่

ฉันต้องทำเสมอหรือไม่if boolean == trueถ้าฉันทำงานกับ Bool เสริม

เนื่องจาก Optionals ไม่สอดคล้องBooleanTypeอีกต่อไปคอมไพเลอร์ไม่ควรรู้ว่าฉันต้องการตรวจสอบค่าของ Bool หรือไม่?


เนื่องจากบูลีนเป็นไปตามโปรโตคอลที่เท่าเทียมกันคุณจึงสามารถเปรียบเทียบตัวเลือกกับตัวเลือกที่ไม่ใช่ทางเลือกได้ ดูที่นี่
น้ำผึ้ง

คำตอบ:


197

ด้วยบูลีนที่เป็นทางเลือกจำเป็นต้องทำให้การตรวจสอบชัดเจน:

if boolean == true {
    ...
}

มิฉะนั้นคุณสามารถแกะอุปกรณ์เสริม:

if boolean! {
    ...
}

แต่จะสร้างข้อยกเว้นรันไทม์หากบูลีนคือnil- เพื่อป้องกันสิ่งนั้น:

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

ก่อนเบต้า 5 เป็นไปได้ แต่มีการเปลี่ยนแปลงตามที่รายงานในบันทึกประจำรุ่น:

Optionals จะไม่ประเมินค่าเป็นจริงโดยปริยายอีกต่อไปเมื่อมีค่าและเป็นเท็จเมื่อไม่มีเพื่อหลีกเลี่ยงความสับสนเมื่อทำงานกับค่า Bool ที่เป็นทางเลือก ให้ทำการตรวจสอบอย่างชัดเจนกับ nil ด้วยตัวดำเนินการ == หรือ! = เพื่อดูว่าตัวเลือกมีค่าหรือไม่

ภาคผนวก: ตามที่แนะนำโดย @MartinR รูปแบบที่กะทัดรัดยิ่งขึ้นสำหรับตัวเลือกที่ 3 คือการใช้ตัวดำเนินการรวมกัน:

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

ซึ่งหมายความว่า: ถ้าบูลีนไม่ใช่ศูนย์นิพจน์จะประเมินเป็นค่าบูลีน (เช่นใช้ค่าบูลีนที่ไม่ได้ปิดกั้น) มิฉะนั้นนิพจน์จะประเมินเป็น false


4
ตัวเลือกที่สามเป็นวิธีการแก้ปัญหาที่ต้องการเนื่องจากเป็นวิธีที่ดีที่สุดในการแสดงเจตนาของรหัส การใช้if letก็ใช้ได้เช่นกัน
Sulthan

29
แตกต่างจากตัวเลือกที่สามโดยใช้"ผู้ประกอบ coalescing ศูนย์ if boolean ?? false { ... }??"
Martin R

4
แต่ถ้าฉันต้องการลบล้างมันก็เริ่มดูไร้สาระ: if !(boolean ?? true) { ... }:(
Andreas

2
บังคับให้คลายความคิดที่ไม่ดีอย่างที่สุด ควรหลีกเลี่ยงเสมอ
Matthieu Riegler

5
ตัวเลือกแรกผิดอะไร สำหรับฉันดูเหมือนว่าเป็นวิธีที่ดีที่สุด
Vahid Amiri

43

การผูกเพิ่มเติม

Swift 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

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)'")
}

โค้ดlet booleanValue = booleanValueจะส่งคืนfalseif booleanValueis nilและifบล็อกไม่ทำงาน หากbooleanValueไม่เป็นnilเช่นนั้นรหัสนี้จะกำหนดตัวแปรใหม่ชื่อbooleanValueประเภทBool(แทนที่จะเป็นทางเลือกBool?)

โค้ด Swift 3 & 4 booleanValue(และโค้ด Swift 2.2 where booleanValue) จะประเมินbooleanValue: Boolตัวแปรใหม่ หากเป็นจริงifบล็อกจะดำเนินการกับbooleanValue: Boolตัวแปรที่กำหนดใหม่ในขอบเขต (อนุญาตให้อ็อพชันอ้างอิงค่าที่ถูกผูกไว้อีกครั้งภายในifบล็อก)

หมายเหตุ: เป็นแบบแผนของ Swift ในการตั้งชื่อค่าคงที่ / ตัวแปรที่ถูกผูกไว้เหมือนกับค่าคงที่ / ตัวแปรทางเลือกเช่นlet booleanValue = booleanValue. เทคนิคนี้เรียกว่าแชโดว์ตัวแปร let unwrappedBooleanValue = booleanValue, unwrappedBooleanValueคุณสามารถทำลายจากการประชุมและใช้สิ่งที่ต้องการ ฉันชี้ให้เห็นสิ่งนี้เพื่อช่วยให้เข้าใจว่าเกิดอะไรขึ้น ฉันขอแนะนำให้ใช้การสร้างเงาตัวแปร

 

แนวทางอื่น ๆ

ไม่มีการรวมตัวกัน

การรวมกันของ Nil นั้นชัดเจนสำหรับกรณีนี้โดยเฉพาะ

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

การตรวจสอบfalseไม่ชัดเจนเท่า

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

หมายเหตุ: if !booleanValue ?? falseไม่ได้รวบรวม

 

บังคับให้คลายตัวเลือก (หลีกเลี่ยง)

การบังคับให้แกะกล่องเพิ่มโอกาสที่ใครบางคนจะทำการเปลี่ยนแปลงในอนาคตที่คอมไพล์ แต่ขัดข้องขณะรันไทม์ ดังนั้นฉันจะหลีกเลี่ยงสิ่งนี้:

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

 

แนวทางทั่วไป

แม้ว่าคำถามเกี่ยวกับสแต็กล้นนี้จะถามถึงวิธีการตรวจสอบว่า a Bool?อยู่trueในifคำสั่งโดยเฉพาะหรือไม่ แต่การระบุวิธีการทั่วไปว่าจะตรวจสอบหาจริงเท็จหรือรวมค่าที่ไม่ได้ใส่ไว้กับนิพจน์อื่น

เมื่อนิพจน์มีความซับซ้อนมากขึ้นฉันพบว่าแนวทางการผูกทางเลือกมีความยืดหยุ่นและเข้าใจง่ายกว่าวิธีอื่น ๆ โปรดทราบว่าการทำงานที่มีผลผูกพันกับตัวเลือกเลือกประเภทใด ๆ ( Int?, String?ฯลฯ )


ฉันมีปัญหาในการใช้นิพจน์บูลีนกับตัวเลือกสำหรับ while ลูป ตัวดำเนินการรวมศูนย์ทำงานได้ แต่ยุ่งและเกิดข้อผิดพลาดได้ง่าย มีวิธีใช้if letไหม?
jbaraga

@jbaraga โปรดโพสต์ตัวอย่าง while loop ที่คุณสงสัย
Mobile Dan

ในการใช้อาร์เรย์เป็นสแต็กฉันต้องการปิดค่าจนกว่าจะตรงตามเงื่อนไขหรือสแต็กว่างเปล่า ตัวอย่างเช่นwhile array.last < threshold { array.removeLast() }
jbaraga

คุณสามารถดำเนินการประมวลผลสแต็กให้สำเร็จได้ด้วยการif, let, whereใช้สิ่งนี้: while let last = array.last where last < threshold { array.removeLast() }ใน Swift 2 หรือwhile let last = array.last, last < threshold { array.removeLast() }ใน Swift 3
Mobile Dan

ดีกว่าขอบคุณ ฉันไม่รู้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")
}

0

ฉันพบวิธีแก้ปัญหาอื่นโดยใช้ตัวดำเนินการบูลีนมากเกินไป ตัวอย่างเช่น:

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

สิ่งนี้อาจไม่ได้อยู่ใน "จิตวิญญาณ" ของการเปลี่ยนแปลงภาษาโดยสิ้นเชิง แต่ช่วยให้สามารถคลายตัวเลือกต่างๆได้อย่างปลอดภัยและสามารถใช้ได้กับเงื่อนไขทุกที่รวมถึงในขณะที่วนซ้ำ


1
ขออภัยเมื่อมองย้อนกลับไปที่โพสต์เดิมมันไม่ได้ตอบคำถามเฉพาะนั้น แต่เป็นคำถามที่ฉันตั้งไว้ในความคิดเห็นก่อนหน้านี้
jbaraga

ฉันจะระมัดระวังอย่างมากเกี่ยวกับการใช้โอเวอร์โหลดนี้เนื่องจากอาจมีบางกรณีที่คุณไม่ต้องการให้ค่าศูนย์ถูกถือว่า "มากกว่า" เป็นค่าที่ไม่ใช่ศูนย์ (คุณอาจต้องการผลลัพธ์ที่ตรงกันข้ามในบางบริบทหรืออาจเป็นทางเลือกก็ได้ การจัดการทั้งหมด) การใช้การแกะกล่องแบบปกติแทนที่จะเป็นการบังคับให้คุณระบุวิธีการจัดการกับปัญหาอย่างชัดเจนในทุกกรณีดังนั้นคุณจึงมีโอกาสน้อยที่จะพบกับผลลัพธ์ที่ไม่คาดคิด
John Montgomery

0

คำตอบที่ฉันพบว่าอ่านง่ายที่สุดคือการกำหนดฟังก์ชัน ไม่ซับซ้อนมาก แต่ได้ผล

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

การใช้งาน:

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

0

ดังที่อันโตนิโอกล่าว

Optionals จะไม่ประเมินค่าเป็นจริงโดยปริยายอีกต่อไปเมื่อมีค่าและเป็นเท็จเมื่อไม่มีเพื่อหลีกเลี่ยงความสับสนเมื่อทำงานกับค่า Bool ที่เป็นทางเลือก ให้ทำการตรวจสอบอย่างชัดเจนกับ nil ด้วยตัวดำเนินการ == หรือ! = เพื่อดูว่าตัวเลือกมีค่าหรือไม่

ฉันใช้เวลาสองสามชั่วโมงในการพยายามทำความเข้าใจบรรทัดของโค้ดที่ฉันสะดุด แต่หัวข้อนี้ทำให้ฉันมาถูกทาง

อ้างนี้เป็นจากสิงหาคม 2014และตั้งแต่นั้นมา Apple เปิดตัวNeverต่อไปนี้ข้อเสนอของ SE-0102และหลังทำให้มันเป็นไปตามEquatable, Hashable ข้อผิดพลาดและเทียบเคียง

ตอนนี้สามารถตรวจสอบได้ว่าบูลีนกำลังnilใช้Never?:


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

คุณสามารถใช้ประเภทอื่น ๆ ที่ไม่สามารถอาศัยอยู่ได้ :

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

ดังที่กล่าวไปแล้วตอนนี้ยังสามารถใช้เครื่องห่อคุณสมบัติได้

@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"

หรือแม้กระทั่ง:

@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"

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.