自定义运算符来简化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约束是不可能的。

这个问题(正如你已经正确的认识到的那样)是因为左边的参数是Ttypes的,所以当你传入一个可选参数时, 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文档