Kiểm tra giá trị của Bool Tùy chọn


88

Khi tôi muốn kiểm tra xem một Bool tùy chọn có đúng là sự thật hay không, việc này không hiệu quả:

var boolean : Bool? = false
if boolean{
}

Nó dẫn đến lỗi này:

Loại tùy chọn '@IvalueBool?' không thể được sử dụng như một boolean; thay vào đó kiểm tra '! = nil'

Tôi không muốn kiểm tra nil; Tôi muốn kiểm tra xem giá trị trả về có đúng không.

Tôi có luôn phải làm gì if boolean == truenếu tôi đang làm việc với Bool tùy chọn không?

Vì Tùy chọn không còn phù hợp BooleanTypenữa, nên trình biên dịch không biết rằng tôi muốn kiểm tra giá trị của Bool sao?


Vì Boolean tuân theo giao thức Equatable, nên bạn có thể so sánh một tùy chọn với một không tùy chọn. Xem ở đây
Honey,

Câu trả lời:


192

Với boolean tùy chọn, nó cần để làm cho việc kiểm tra rõ ràng:

if boolean == true {
    ...
}

Nếu không, bạn có thể mở gói tùy chọn:

if boolean! {
    ...
}

Nhưng điều đó tạo ra một ngoại lệ thời gian chạy nếu boolean nil- để ngăn điều đó:

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

Trước phiên bản beta 5, có thể thực hiện được, nhưng nó đã được thay đổi như được báo cáo trong ghi chú phát hành:

Các tùy chọn không còn mặc nhiên đánh giá thành true khi chúng có giá trị và false khi không, để tránh nhầm lẫn khi làm việc với các giá trị Bool tùy chọn. Thay vào đó, hãy kiểm tra rõ ràng nil với các toán tử == hoặc! = Để tìm hiểu xem một tùy chọn có chứa giá trị hay không.

Phụ lục: như được đề xuất bởi @MartinR, một biến thể nhỏ gọn hơn cho tùy chọn thứ 3 là sử dụng toán tử liên kết:

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

có nghĩa là: nếu boolean không phải là nil, biểu thức đánh giá thành giá trị boolean (tức là sử dụng giá trị boolean không được bao bọc), nếu không thì biểu thức đánh giá là false


4
Tùy chọn thứ ba là giải pháp ưa thích vì nó là cách tốt nhất để thể hiện ý định của mã. Sử dụng if letcũng sẽ hoạt động.
Sulthan

29
Một biến thể của tùy chọn thứ ba, sử dụng "toán tử liên kết nil ??" : if boolean ?? false { ... } .
Martin R

4
Nhưng nếu tôi muốn phủ định nó thì nó bắt đầu trông nực cười: if !(boolean ?? true) { ... }:(
Andreas

2
Buộc bỏ ra một ý tưởng cực kỳ tồi tệ. Nó luôn nên được tránh.
Matthieu Riegler

4
Có gì sai với tùy chọn đầu tiên? Đối với tôi nó có vẻ như là cách tốt nhất.
Vahid Amiri

43

Ràng buộc tùy chọn

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 = booleanValuetrả về falsenếu booleanValuenilifkhối không thực thi. Nếu booleanValuekhông nil, mã này xác định một biến mới có tên booleanValuekiểu Bool(thay vì tùy chọn, Bool?).

Mã Swift 3 & 4 booleanValue(và mã Swift 2.2 where booleanValue) đánh giá booleanValue: Boolbiến mới . Nếu đúng, ifkhối sẽ thực thi với biến mới được xác định booleanValue: Booltrong phạm vi (cho phép tùy chọn tham chiếu lại giá trị được ràng buộc trong ifkhối).

Lưu ý: Đó là một quy ước Swift để đặt tên cho hằng / biến bị ràng buộc giống với hằng / biến tùy chọn chẳng hạn let booleanValue = booleanValue. Kỹ thuật này được gọi là tạo bóng thay đổi . Bạn có thể phá vỡ quy ước và sử dụng một cái gì đó như thế let unwrappedBooleanValue = booleanValue, unwrappedBooleanValue. Tôi chỉ ra điều này để giúp hiểu những gì đang xảy ra. Tôi khuyên bạn nên sử dụng bóng thay đổi.

 

Các cách tiếp cận khác

Nil kết hợp

Nil kết hợp là rõ ràng cho trường hợp cụ thể này

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

Kiểm tra falsekhông rõ ràng

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

Lưu ý: if !booleanValue ?? falsekhông biên dịch.

 

Buộc mở gói tùy chọn (tránh)

Buộc mở gói làm tăng khả năng ai đó sẽ thực hiện thay đổi trong tương lai mà biên dịch nhưng bị lỗi trong thời gian chạy. Do đó, tôi sẽ tránh những thứ như thế này:

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

 

Cách tiếp cận chung

Mặc dù câu hỏi tràn ngăn xếp này hỏi cụ thể cách kiểm tra xem a Bool?truenằm trong một ifcâu lệnh hay không, nhưng sẽ rất hữu ích khi xác định một cách tiếp cận chung cho dù kiểm tra đúng, sai hoặc kết hợp giá trị không được bao bọc với các biểu thức khác.

Khi biểu thức trở nên phức tạp hơn, tôi thấy cách tiếp cận ràng buộc tùy chọn linh hoạt hơn và dễ hiểu hơn các cách tiếp cận khác. Lưu ý rằng công trình ràng buộc bắt buộc với bất kỳ loại tùy chọn ( Int?, String?, vv).


Tôi đang gặp khó khăn khi sử dụng biểu thức Boolean với vòng lặp for while tùy chọn. Toán tử liên kết nil hoạt động, nhưng nó lộn xộn và dễ xảy ra lỗi. Có một cách để sử dụng if let?
jbaraga 24/09/2016

@jbaraga, vui lòng đăng một ví dụ về vòng lặp while mà bạn đang thắc mắc.
Mobile Dan

Khi sử dụng một mảng làm ngăn xếp, tôi muốn bật ra các giá trị cho đến khi một điều kiện được đáp ứng hoặc ngăn xếp trống. Ví dụ:while array.last < threshold { array.removeLast() }
jbaraga 24/09/2016

Bạn có thể thực hiện điều đó xử lý ngăn xếp với if, let, wheresử dụng này: while let last = array.last where last < threshold { array.removeLast() }trong Swift 2 hoặc while let last = array.last, last < threshold { array.removeLast() }trong Swift 3.
Điện thoại di động Dan

Đó là tốt hơn, cảm ơn. Tôi đã không nhận thức được while let.
jbaraga

1
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

Tôi đã tìm thấy một giải pháp khác, làm quá tải các toán tử Boolean. Ví dụ:

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

Điều này có thể không hoàn toàn theo "tinh thần" của các thay đổi ngôn ngữ, nhưng nó cho phép mở gói an toàn các tùy chọn và nó có thể sử dụng cho các điều kiện ở bất kỳ đâu, kể cả vòng lặp while.


1
Xin lỗi, nhìn lại bài viết ban đầu, nó không trả lời câu hỏi cụ thể đó, mà là câu hỏi tôi đã nêu ra trong nhận xét trước đó của mình.
jbaraga 24/09/2016

Tôi sẽ rất cẩn thận về việc sử dụng quá tải này, vì có thể có trường hợp bạn không muốn nil được coi là "lớn hơn" một giá trị không phải nil (bạn có thể muốn kết quả ngược lại trong các ngữ cảnh nhất định hoặc có thể thay thế xử lý hoàn toàn). Thay vào đó, việc sử dụng chức năng mở gói thông thường buộc bạn phải giải quyết rõ ràng cách bạn muốn xử lý các lỗ hổng trong mọi trường hợp, vì vậy bạn ít có khả năng gặp phải kết quả không mong muốn.
John Montgomery

0

Câu trả lời tôi thấy dễ đọc nhất là định nghĩa một hàm. Không phải là rất phức tạp nhưng làm công việc.

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

sử dụng:

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

0

Như Antonio nói

Các tùy chọn không còn mặc nhiên đánh giá thành true khi chúng có giá trị và false khi không, để tránh nhầm lẫn khi làm việc với các giá trị Bool tùy chọn. Thay vào đó, hãy kiểm tra rõ ràng nil với các toán tử == hoặc! = Để tìm hiểu xem một tùy chọn có chứa giá trị hay không.

Tôi đã dành vài giờ để cố gắng hiểu một dòng mã mà tôi tình cờ gặp phải, nhưng chuỗi này đã đưa tôi đi đúng hướng.

Báo giá này là từ tháng 8 năm 2014 , và kể từ đó Apple đã giới thiệu đề xuấtNever sau SE-0102 và sau đó làm cho nó phù hợp với Equatable, Hashable, Error và Comporing

Bây giờ có thể kiểm tra xem boolean có đang nilsử dụng hay không Never?:


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

Bạn thực sự có thể sử dụng bất kỳ loại không thể ở được nào khác:

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

Điều đó đang được nói, bây giờ cũng có thể sử dụng trình bao bọc thuộc tính:

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

hoặc thậm chí:

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.