我试图在Swift 3中创建一个null-coalescing赋值运算符。换句话说,而不是:
x = x ?? y
我想要这个:
x ??= y
Swift 3似乎不喜欢我的操作员。这是它的定义:
infix operator ??= : AssignmentPrecedence
func ??=(lhs: inout Any?, rhs: @autoclosure () -> Any?) {
if lhs != nil { return }
lhs = rhs()
}
var x = 3
x ??= 7 // Cannot convert value of type 'Int' to expected argument type 'inout Any?'.
我在没有@autoclosure
的情况下完成了它。我的理解是优先组AssignmentPrecedence
已经包含assignment
声明,所以这不太可能是问题。
如何在Swift 3中执行此操作?
答案 0 :(得分:5)
首先,使运算符通用而不是使用Any
:
infix operator ??= : AssignmentPrecedence
func ??=<T>(lhs: inout T?, rhs: @autoclosure () -> T?) {
if lhs != nil { return }
lhs = rhs()
}
其次,左操作数需要是可选的(否则它
无法针对nil
)进行测试:
var x: Int? = 3
x ??= 7
答案 1 :(得分:1)
我想出了自己的“空值销售”赋值运算符的“风格”,类似于上面的内容,但是通过添加一个返回类型,您既可以同时进行赋值又可以返回,这与正常情况更加一致'??'行为,如果LHS为空,则从RHS到LHS分配分配。非常适合可重置的懒惰变量。这是我的版本...
// Null-coalescing assignment operator
infix operator ??= : AssignmentPrecedence
@discardableResult
func ??= <T>(lhs:inout T?, rhs:@autoclosure () -> T) -> T {
if let lhs = lhs {
return lhs
}
let rhsResult = rhs()
lhs = rhsResult
return rhsResult
}
有了上面的内容,我现在可以像这样做可重置的懒惰变量了……
private var qCache:Int?
var q:Int {
return qCache ??= {
print("Lazy-calculating q...")
return 44
}()
}
func resetQ() { qCache = nil }
print("1: q is \(q)")
print("2: q is \(q)")
print("3: q is \(q)")
print("Resetting lazy q...")
resetQ()
print("4: q is \(q)")
输出...
Lazy-calculating q...
1: q is 44
2: q is 44
3: q is 44
Resetting lazy q...
Lazy-calculating q...
4: q is 44
如果您愿意的话,也可以实现setter并分离出“惰性计算”功能...
func lazyCalcQ() -> Int {
print("Lazy-calculating q...")
return 44
}
private var qCache:Int?
var q:Int {
get { return qCache ??= lazyCalcQ() }
set { qCache = newValue }
}
func resetQ() { qCache = nil }
更进一步,您可以使用隐式展开的数据类型,以便可以使用nil
的赋值进行重置,但始终可以确保从getter获得值。唯一的缺点是您有时仍必须强制将其拆开以使某些警告消失,例如下面的打印语句,但同样,可以保证您有一个值,因此这样做是安全的。
注意:就我个人而言,我更喜欢上面的方法使用非隐式展开版本和额外的“重置”功能-特别是如果它是只读属性-因为它使API更加清晰,但是我将其分享为完整起见,因为它显示了nil-as-reset的创造性用法。
func lazyCalcR() -> Int {
print("Lazy-calculating r (i.e. the default when reset with 'nil')...")
return 10
}
private var rCache:Int?
var r:Int! {
get { return rCache ??= lazyCalcR() }
set { rCache = newValue }
}
print("1: r is \(r!)")
r += 10
print("2: r is \(r!)")
r += 10
print("3: r is \(r!)")
print("Resetting r to default...")
r = nil
print("4: r is \(r!)")
输出...
Lazy-calculating r (i.e. the default when reset with 'nil')...
1: r is 10
2: r is 20
3: r is 30
Resetting r to default...
Lazy-calculating r (i.e. the default when reset with 'nil')...
4: r is 10
当然,以上都是使用int的简单示例,但是我用它来执行诸如基于边界计算复杂路径之类的事情。
我的下一个尝试是将所有这些隐藏在属性包装器中,从而消除对该操作符本身的需要。