我有以下代码:
protocol TestProtocol {
var string: String {get}
}
struct TestStructOne : TestProtocol {
let string = "test string"
var stringTwo = "test string three"
}
struct TestStructTwo : TestProtocol {
let string = "test string two"
}
var testStructOnes = [TestStructOne(), TestStructOne(), TestStructOne()]
// works
var protocolArrayOne: [TestProtocol] = [TestProtocol]()
for testStruct in testStructOnes.filter({ $0.stringTwo == "test string three" }) {
protocolArrayOne.append(testStruct)
}
// does not work, cannot copnvert value of type '[TestStructOne]' to specified type '[TestProtocol]'
var protocolArrayTwo: [TestProtocol] = testStructOnes.filter({ $0.stringTwo == "test string three" })
我不明白为什么最后一行不起作用。任何人都可以填补我吗?我没有看到它与迭代数组并手动添加每个元素有什么不同。
答案 0 :(得分:3)
这是由于Swift数组在隐式转换它们的类型时受到限制,这是Swift中泛型不变性的结果,并且讨论得更多in this Q&A。
解决方案的关键是你无法将数组直接从[TestStructOne]
转换为[TestProtocol]
- 而是必须分别转换每个元素,这就是为什么你的方法使用for循环和手动将元素附加到新数组上。
使用函数式编程解决此问题的最简单方法通常是使用map
以允许每个元素上传到TestProtocol
(Swift可以从显式类型注释中推断出这一点) :
let protocolArrayTwo: [TestProtocol] = testStructOnes.filter {
$0.stringTwo == "test string three"
}.map{$0}
然而,在你的情况下,这是低效的,因为你不得不两次迭代你的数组,顺便提一下你的for
循环正在做什么(过滤然后循环元素)。
相反,您可以使用具有给定for
条件的where
循环,以便通过在单次迭代中将它们附加到新数组来过滤掉所需的元素:
var protocolArray = [TestProtocol]()
for testStruct in testStructOnes where testStruct.stringTwo == "test string three" {
protocolArray.append(testStruct)
}
或者如果您喜欢函数式编程,可以使用flatMap
来过滤掉您不想要的元素(通过利用flatMap
过滤掉{{1}的事实})并将它们转换为您的协议类型:
nil
与let protocolArray : [TestProtocol] = testStructOnes.flatMap {
$0.stringTwo == "test string three" ? $0 : nil
}
非常相似,此处的显式类型注释允许map
将每个元素隐式转换为flatMap
。