Swift 可选提升与通用重载决议
Swift optional promotion vs generic overload resolution
请考虑以下代码:
protocol P {}
class X {}
class Y: P {}
func foo<T>(_ closure: (T) -> Void) { print(type(of: closure)) }
func foo<T>(_ closure: (T) -> Void) where T: P { print(type(of: closure)) }
let xClosure: (X?) -> Void = { _ in }
foo(xClosure) // prints "(Optional<X>) -> ()"
let yClosure: (Y?) -> Void = { _ in }
foo(yClosure) // prints "(Y) -> ()"
为什么 foo(yClosure)
调用解析为 foo
的版本限制为 T: P
?
我明白为什么那个版本会打印它打印的内容,
我没有看到的是为什么它被调用而不是另一个。
对我来说,非 P 版本似乎更适合 T == (Y?) -> Void
。
当然,约束版本更具体,但需要转换
(从 (Y?) -> Void
到 (Y) -> Void
的隐式转换),
而非P版本可以调用,无需转换。
有没有办法修复此代码,以便调用 P 约束版本
只有传入的闭包的参数类型直接符合P,
没有任何隐式转换?
根据我的实验,特异性似乎总是胜过方差转换。例如:
func bar<T>(_ x: [Int], _ y: T) { print("A") }
func bar<T: P>(_ x: [Any], _ y: T) { print("B") }
bar([1], Y()) // A
bar
更具体,但需要从 [Int]
到 [Any]
.
的方差转换
为什么可以从 (Y?) -> Void
转换为 (P) -> Void
,请参阅 。请注意,Y
是 Y?
的子类型,这是编译器的魔法。
既然如此一致,这种行为似乎是设计使然。因为你不能真正让 Y
not 成为 Y?
的子类型,所以如果你想获得所需的行为,你没有太多选择。
我有这个解决方法,我承认它真的很难看 - 制作您自己的 Optional
类型。我们称它为 Maybe<T>
:
enum Maybe<T> {
case some(T)
case none
// implement all the Optional methods if you want
}
现在,您的 (Maybe<Y>) -> Void
不会转换为 (P) -> Void
。通常我不会推荐这个,但既然你说:
in the real-world code where I encountered this, the closure has multiple params, any of them can be optional, so this would lead to a combinatorial explosion.
我认为重塑 Optional
可能 是值得的。
请考虑以下代码:
protocol P {}
class X {}
class Y: P {}
func foo<T>(_ closure: (T) -> Void) { print(type(of: closure)) }
func foo<T>(_ closure: (T) -> Void) where T: P { print(type(of: closure)) }
let xClosure: (X?) -> Void = { _ in }
foo(xClosure) // prints "(Optional<X>) -> ()"
let yClosure: (Y?) -> Void = { _ in }
foo(yClosure) // prints "(Y) -> ()"
为什么 foo(yClosure)
调用解析为 foo
的版本限制为 T: P
?
我明白为什么那个版本会打印它打印的内容,
我没有看到的是为什么它被调用而不是另一个。
对我来说,非 P 版本似乎更适合 T == (Y?) -> Void
。
当然,约束版本更具体,但需要转换
(从 (Y?) -> Void
到 (Y) -> Void
的隐式转换),
而非P版本可以调用,无需转换。
有没有办法修复此代码,以便调用 P 约束版本 只有传入的闭包的参数类型直接符合P, 没有任何隐式转换?
根据我的实验,特异性似乎总是胜过方差转换。例如:
func bar<T>(_ x: [Int], _ y: T) { print("A") }
func bar<T: P>(_ x: [Any], _ y: T) { print("B") }
bar([1], Y()) // A
bar
更具体,但需要从 [Int]
到 [Any]
.
为什么可以从 (Y?) -> Void
转换为 (P) -> Void
,请参阅 Y
是 Y?
的子类型,这是编译器的魔法。
既然如此一致,这种行为似乎是设计使然。因为你不能真正让 Y
not 成为 Y?
的子类型,所以如果你想获得所需的行为,你没有太多选择。
我有这个解决方法,我承认它真的很难看 - 制作您自己的 Optional
类型。我们称它为 Maybe<T>
:
enum Maybe<T> {
case some(T)
case none
// implement all the Optional methods if you want
}
现在,您的 (Maybe<Y>) -> Void
不会转换为 (P) -> Void
。通常我不会推荐这个,但既然你说:
in the real-world code where I encountered this, the closure has multiple params, any of them can be optional, so this would lead to a combinatorial explosion.
我认为重塑 Optional
可能 是值得的。