swift 泛型 : Cannot convert value of type to expected argument type

标签 swift generics

这是我的代码:

protocol SomeProtocol {
}

class A: SomeProtocol {
}

func f1<T: SomeProtocol>(ofType: T.Type, listener: (T?) -> Void) {
}

func f2<T: SomeProtocol>(ofType: T.Type, listener: ([T]?) -> Void) {
}

func g() {
    let l1: (SomeProtocol?) -> Void = ...
    let l2: ([SomeProtocol]?) -> Void = ...
    f1(ofType: A.self, listener: l1) // NO ERROR
    f2(ofType: A.self, listener: l2) // COMPILE ERROR: Cannot convert value of type '([SomeProtocol]?) -> Void' to expected argument type '([_]?) -> Void'
}

第二个闭包的参数是泛型对象的数组,这有什么问题?

最佳答案

swift 4.1 更新

这是一个已修复的错误 in this pull request ,这将使其成为 Swift 4.1 的发布版本。您的代码现在可以在 4.1 快照中按预期进行编译。


Swift 4.1 之前

这看起来就像您只是将编译器扩展得太远了。

  • 它可以处理从子类型元素数组到父类(super class)型元素数组的转换,例如 [A][SomeProtocol] – 这个是协方差。值得注意的是,数组在这里一直是一种边缘情况,因为任意泛型是不变的。某些集合,例如 Array,只是 get special treatment from the compiler允许协方差。

  • 它可以处理具有父类(super class)型参数的函数到具有子类型参数的函数的转换,例如 (SomeProtocol) -> Void(A) -> Void – 这是逆变。

然而,它目前似乎无法同时完成这两项工作(但实际上它应该能够;请随意 file a bug)。

就其值(value)而言,这与泛型无关,以下重现了相同的行为:

protocol SomeProtocol {}
class A : SomeProtocol {}

func f1(listener: (A) -> Void) {}
func f2(listener: ([A]) -> Void) {}
func f3(listener: () -> [SomeProtocol]) {}

func g() {

    let l1: (SomeProtocol) -> Void = { _ in }        
    f1(listener: l1) // NO ERROR

    let l2: ([SomeProtocol]) -> Void = { _ in }
    f2(listener: l2) 
    // COMPILER ERROR: Cannot convert value of type '([SomeProtocol]) -> Void' to
    // expected argument type '([A]) -> Void'

    // it's the same story for function return types
    let l3: () -> [A] = { [] }
    f3(listener: l3)
    // COMPILER ERROR: Cannot convert value of type '() -> [A]' to
    // expected argument type '() -> [SomeProtocol]'
}

在修复之前,这种情况下的一种解决方案是简单地使用闭包表达式充当两种函数类型之间的蹦床:

// converting a ([SomeProtocol]) -> Void to a ([A]) -> Void.
// compiler infers closure expression to be of type ([A]) -> Void, and in the
// implementation, $0 gets implicitly converted from [A] to [SomeProtocol].
f2(listener: { l2($0) })

// converting a () -> [A] to a () -> [SomeProtocol].
// compiler infers closure expression to be of type () -> [SomeProtocol], and in the
// implementation, the result of l3 gets implicitly converted from [A] to [SomeProtocol]
f3(listener: { l3() })

并且,应用于您的代码:

f2(ofType: A.self, listener: { l2($0) })

这是有效的,因为编译器将闭包表达式推断为 ([T]?) -> Void 类型,可以将其传递给 f2。在闭包的实现中,编译器随后将 $0[T]? 隐式转换为 [SomeProtocol]?

并且,as Dominik is hinting at ,这个蹦床也可以作为 f2 的额外重载来完成:

func f2<T : SomeProtocol>(ofType type: T.Type, listener: ([SomeProtocol]?) -> Void) {
    // pass a closure expression of type ([T]?) -> Void to the original f2, we then
    // deal with the conversion from [T]? to [SomeProtocol]? in the closure.
    // (and by "we", I mean the compiler, implicitly)
    f2(ofType: type, listener: { (arr: [T]?) in listener(arr) })
}

允许您再次将其称为 f2(ofType: A.self, listener: l2)

关于 swift 泛型 : Cannot convert value of type to expected argument type,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44991116/

相关文章:

c# - 如何解决 C# 反射中泛型方法的歧义

ios - Swift:继承 UITextView 或 UICollectionView 并正确初始化

c# - 泛型类中的隐式类型参数

指向 const char 参数指针的 Swift 指针

ios - 如何使用自动布局基于 webview 内容管理 UITableHeaderView 的高度

c# - 避免泛型类型的模糊调用错误

ios - Parse Xamarin SDK 会显着增加 iOS 应用程序的应用程序大小

c# - Linq to SQL 中的 DAO 工厂

swift - 如何从容器 View 呈现 View Controller ?

ios - 在将iOS设备切换为横向模式时调整Youtube iFrame的大小