我正在尝试编写一个具有符合MyProto
的对象数组的类,并且我有一种方法可以在添加到该数组之前接受[MyProto]
进行各种处理。这是一个游乐场。
protocol MyProto {
func sayHello()
}
extension MyProto {
func sayHello() {
print("hello")
}
}
struct MyStruct: MyProto {
}
class MyClass {
var protos: [MyProto] = []
func doSomethingAndThenStore(newProtos: [MyProto]) {
for proto in newProtos {
proto.sayHello()
}
protos.appendContentsOf(newProtos)
}
}
let myStructs = [MyStruct(), MyStruct()]
let myClass = MyClass()
myClass.doSomethingAndThenStore(myStructs)
在最后一行,我收到错误error: cannot convert value of type '[MyStruct]' to expected argument type '[MyProto]'
。如果我将其更改为myStructs as [MyProto]
,则错误会更改为error: cannot convert value of type '[MyStruct]' to type '[MyProto]' in coercion
。
如何将我的具体类型数组传递给接受一组协议的方法?
答案 0 :(得分:3)
这个问题源于Swift不支持协变泛型的事实。也就是说,Array<Subclass>
不是Array<Superclass>
。在这种情况下,即使MyStruct
是MyProto
,Array<MyStruct>
也不是Array<MyProto>
。
Swift不支持这一点的原因有点复杂,但归结为对于某些操作,例如数组检索,将Array<MyStruct>
视为Array<MyProto>
是有效的,但对于其他的,例如数组插入,这种关联实际上是相反的。您将无法在MyProto
中插入Array<MyStruct>
,因此Array<MyStruct>
不能被视为Array<MyProto>
。其他语言有解决此问题的机制,但Swift目前不支持它们。
您无法直接传递数组,但此限制有几种解决方法。最简单的说,您可以在数组上映射标识函数,以便类型检查器推断出新类型。这将隐式地将每个元素从MyStruct
单独转发到MyProto
:
myClass.doSomethingAndThenStore(myStructs.map { $0 })
您也可以将MyClass设为通用并添加类型约束:
class MyClass<T: MyProto> {
var protos: [T] = []
func doSomethingAndThenStore(newProtos: [T]) {
for proto in newProtos {
proto.sayHello()
}
protos.appendContentsOf(newProtos)
}
}