Swift: testowanie wartości opcjonalnej w obudowie przełącznika

94

W języku Swift, w jaki sposób mogę napisać przypadek w instrukcji switch, która testuje przełączaną wartość względem zawartości opcjonalnej , pomijając wielkość liter, jeśli opcja zawiera nil?

Oto jak to sobie wyobrażam:

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
}

Jeżeli po prostu napisać go dokładnie tak, kompilator narzeka, że someOptionalnie jest rozpakowany, ale gdybym nim wyraźnie unwrap dodając !do końca, ja oczywiście się błąd wykonania o każdej porze someOptionalzawiera nil. Dodanie ?zamiast tego !miałoby dla mnie jakiś sens (w duchu opcjonalnego łączenia w łańcuch, jak przypuszczam), ale nie usuwa błędu kompilatora (tj. Faktycznie nie rozpakowuje opcjonalnego).

George WS
źródło

Odpowiedzi:

113

Opcjonalne jest po prostu enumtakie:

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

    // ...
}

Możesz więc dopasować je jak zwykle do wzorców dopasowania „Wartości powiązane” :

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

Jeśli chcesz dopasować z someValue, używając wyrażenia ochronnego :

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

A dla Swift> 2.0

switch someValue {
case let val where val == someOptional:
    print("matched")
default:
    print("didn't match; default")        
}
rintaro
źródło
5
Zauważ, że w Swift 3 niektóre / none są małe, tj. Użyjesz .some zamiast .Some
Adam
55

Począwszy od Xcode 7, „nowy x?wzorzec może być używany do dopasowania wzorca do opcji jako synonim .some(x)”. Oznacza to, że w Swift 2 i późniejszych działa również następująca odmiana odpowiedzi rintaro :

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
źródło
3
Pytanie dotyczy dopasowania wartości nieobowiązkowej do wartości opcjonalnej, ta odpowiedź jest odwrotna.
Martin R
2
To prawda, jednak ta odpowiedź została pierwotnie napisana przez PO jako aktualizacja pytania, więc dla niego była to niezbicie wykonalne rozwiązanie; Właśnie przeniosłem to do odpowiedzi wiki społeczności. Być może @GeorgeWS może wyjaśnić, dlaczego zmiana przełącznika i argumentów przypadków działa w jego przypadku użycia?
Slipp D. Thompson,
2
Jestem trochę zagubiony. jaka jest różnica między twoimi pierwszymi dwoma przypadkami? someValue?jest inną zdefiniowaną wartością, ale czy case let val?jest to tylko bezpieczna nieopakowana wersja someOptional?!
Miód
@ Kochanie To nie jest prawdziwy przykład kodu; to po prostu wariacja na temat odpowiedzi rintaro. Więc zadaj mu to pytanie - moja odpowiedź jest funkcjonalnie równoważna z kodem w jego / jej. Gdybyś jednak zapytał rintaro, uważam, że odpowiedź brzmiałaby: 1. odzwierciedla to, co jest w połączonych dokumentach Apple; 2. demonstruje tylko składnię; nie realizuje odrębnego celu obliczeniowego ani logiki biznesowej.
Slipp D. Thompson,
@ Kochanie Również odpowiedź rintaro została pierwotnie napisana dla Swift 1.xi zaktualizowana dla Swift 2. Możliwe, że wersja bez letkompilacji już się nie kompiluje. W tej chwili nie pamiętam, dlaczego to zadziałało kiedyś.
Slipp D. Thompson,
10

W Swift 4 możesz użyć Optional: ExpressibleByNilLiteral firmy Apple, aby opakować opcjonalne

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

Przykład

enum MyEnum {
    case normal
    case cool
}

trochę

let myOptional: MyEnum? = MyEnum.normal

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

    case .none: 
    break

    default:
    break
}

Żaden

let myOptional: MyEnum? = nil

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

    case .none: 
    // Found nil
    break

    default:
    break
}

domyślna

let myOptional: MyEnum? = MyEnum.cool

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

    case .none: 
    break

    default:
    // Found .Cool enum
    break
}

Wyliczenie z wartością

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

jakąś wartość

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
źródło
Wspomniałeś, ExpressibleByNilLiteralale wtedy to nie jest faktycznie używane w deklaracjach wyliczeniowych itp. Co to jest?
pkamb