自定义运算符来简化If-Let
我想简化一直需要做的事情
if let firstName = firstName { self.name = firstName }
一个可能的习惯,通用的操作符来做到这一点
infix operator ?= {} func ?= <T>(inout left: T, right: T?) { if let right = right { left = right } }
简化前面的例子
self.name ?= firstName
这会产生一个问题,如果firstName的值是零 ,那么Swift会把值包装在一个可选项中。
var name: String? = "Bob" var firstName: String? = nil self.name ?= firstName print(self.name) /* prints "nil" since it is wrapping firstName in an optional when passed in. Eg Optional<nil> gets unwrapped to nil in function and assigned */
任何可能的修复自定义运算符? 我试图限制左边的参数不是可选的,但对于generics的types约束是不可能的。
这个问题(正如你已经正确的认识到的那样)是因为左边的参数是T
types的,所以当你传入一个可选参数时, T
将被推断为Optional<Whatever>
。 因为右边的参数是T?
(因为types可以自由地提升为可选项),它会推断types是Optional<Optional<Whatever>>
,导致您正在观察的令人困惑的双重包装。
解决的办法是增加一个重载处理情况,左边的参数也是可选的。
infix operator ?= {} func ?= <T>(inout left: T, right: T?) { if let right = right { left = right } } // overload to deal with an optional left handed side func ?= <T>(inout left: T?, right: T?) { if let right = right { left = right } }
(注意在Swift 3中, inout
应该出现在参数types之前)
现在,如果使用带有左手参数的可选参数的运算符,则Swift将使用重载版本,而不是原始版本,因为它始终支持更多types特定的签名。 这意味着右侧不会包含在double可选项中,因为它现在与左侧参数的types完全相同。
var name: String? = "Bob" var firstName: String? = nil name ?= firstName print(name) // prints: Optional("Bob")
请注意,这是类似于什么??
它有两个定义来处理一方是可选的,一方是非可选的,双方都是可选的,以避免产生双重包装的可选项:
@warn_unused_result public func ??<T>(optional: T?, @autoclosure defaultValue: () throws -> T) rethrows -> T @warn_unused_result public func ??<T>(optional: T?, @autoclosure defaultValue: () throws -> T?) rethrows -> T?
我find了一个有趣的方法来减less由Nil合并运算符产生的开销
nil合并运算符(a ?? b)打开一个可选的a(如果它包含一个值),或者如果a为零,则返回一个默认值b。 expression式a始终是可选的types。 expression式b必须匹配存储在a中的types。
总之 – nonOptional = optional ?? someDefaultValue
nonOptional = optional ?? someDefaultValue
例如 –
let defaultColorName = "red" var userDefinedColorName: String? // optional variable var colorNameToUse = userDefinedColorName ?? defaultColorName
所以,这里如果userDefinedColorName
是零,那么defaultColorName
将被赋值给非可选variablescolorNameToUse
。
有关完整的参考资料,请查阅Swift文档