Swift: prueba con valor opcional en caso de interruptor

94

En Swift, ¿cómo puedo escribir un caso en una declaración de cambio que prueba el valor que se cambia con el contenido de un opcional , omitiendo el caso si el opcional contiene nil?

Así es como me imagino que esto podría verse:

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
}

Si lo escribo exactamente así, el compilador se queja de que someOptionalno se desenvuelve, pero si lo desenvuelvo explícitamente agregando !al final, por supuesto, obtengo un error de tiempo de ejecución en cualquier momento que someOptionalcontenga nil. Agregar en ?lugar de !tendría algún sentido para mí (en el espíritu del encadenamiento opcional, supongo), pero no hace que el error del compilador desaparezca (es decir, en realidad no desenvuelve el opcional).

George WS
fuente

Respuestas:

113

Opcional es enumcomo este:

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

    // ...
}

Por lo tanto, puede hacer coincidir los patrones de coincidencia habituales de "Valores asociados" :

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

Si desea hacer coincidir desde someValue, usando expresión de guardia :

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

Y para Swift> 2.0

switch someValue {
case let val where val == someOptional:
    print("matched")
default:
    print("didn't match; default")        
}
rintaro
fuente
5
Tenga en cuenta que en Swift 3, algunos / ninguno están en minúsculas, es decir, usaría .some en lugar de .Some
Adam
55

A partir de Xcode 7, x?se puede usar un nuevo patrón para emparejar patrones con opcionales como sinónimo de .some(x)”. Esto significa que en Swift 2 y versiones posteriores, la siguiente variación de la respuesta de rintaro también funcionará:

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
fuente
3
La pregunta se trata de hacer coincidir un valor no opcional con uno opcional, esta respuesta es al revés.
Martin R
2
Es cierto, sin embargo, esta respuesta fue escrita originalmente por el OP como una actualización de la pregunta, por lo que para él era irrefutablemente una solución viable; Lo acabo de mover a una respuesta wiki de la comunidad. Tal vez @GeorgeWS pueda aclarar por qué cambiar los argumentos de cambio y caso funciona para su caso de uso.
Slipp D. Thompson
2
Estoy un poco perdido. ¿Cuál es la diferencia entre sus dos primeros casos? someValue?es algún otro valor definido, pero ¿ case let val?es solo la versión segura sin envolver de someOptional?!
Cariño
@Honey No es un ejemplo de código del mundo real; es simplemente una variación de la respuesta de rintaro. Así que hazle esa pregunta, mi respuesta es funcionalmente equivalente al código en el suyo. Sin embargo, si le preguntaras a rintaro, creo que la respuesta sería 1. refleja lo que hay en los documentos de Apple vinculados; 2. solo demuestra la sintaxis; no logra un cálculo distinto o un objetivo de lógica empresarial.
Slipp D. Thompson
@Honey Además, la respuesta de rintaro se escribió originalmente para Swift 1.xy se actualizó para Swift 2. Es posible que la versión sin letya no se compile. No puedo recordar ahora por qué eso habría funcionado en el pasado.
Slipp D. Thompson
10

En Swift 4 puede usar Optional: ExpressibleByNilLiteral de Apple para envolver opcional

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

Ejemplo

enum MyEnum {
    case normal
    case cool
}

algunos

let myOptional: MyEnum? = MyEnum.normal

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

    case .none: 
    break

    default:
    break
}

ninguna

let myOptional: MyEnum? = nil

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

    case .none: 
    // Found nil
    break

    default:
    break
}

defecto

let myOptional: MyEnum? = MyEnum.cool

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

    case .none: 
    break

    default:
    // Found .Cool enum
    break
}

Enum con valor

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

algo de valor

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
fuente
Mencionas, ExpressibleByNilLiteralpero eso no se usa realmente en las declaraciones de enumeración, etc. ¿Qué es eso?
pkamb