如何扩展swift数组以访问特定类型的成员?
如果数组包含从同一个超类继承的多个类的实例,则这是相关的。理想情况下,它会适当地强制执行类型检查。
使用filter(_:)
方法可以正常工作,但确实可以强制执行类型安全。例如:
protocol MyProtocol { }
struct TypeA: MyProtocol { }
struct TypeB: MyProtocol { }
let myStructs:[MyProtocol] = [ TypeA(), TypeA(), TypeB() ]
let filteredArray = myStructs.filter({ $0 is TypeA })
filteredArray
包含正确的值,但类型仍然是[MyProtocol]
而不是[TypeA]
。我希望用let filteredArray = myStructs.filter({ $0 is TypeA }) as! [TypeA]
替换最后一个会解决这个问题,但项目会因{I} EXEC_BAD_INSTRUCTION
而失败,我不明白。也许不能使用类型转换数组?
理想情况下,此行为可以包含在数组扩展中。以下内容无法编译:
extension Array {
func objectsOfType<T:Element>(type:T.Type) -> [T] {
return filter { $0 is T } as! [T]
}
}
这里似乎至少有两个问题:类型约束T:Element
似乎不起作用。我不确定基于泛型类型添加约束的正确方法是什么。我的意图是T
是Element
的子类型。此外,第3行还有编译时错误,但这可能只是传播相同的错误。
答案 0 :(得分:8)
SequenceType
有flatMap()
方法作为&#34;可选过滤器&#34;:
extension SequenceType {
/// Return an `Array` containing the non-nil results of mapping
/// `transform` over `self`.
///
/// - Complexity: O(*M* + *N*), where *M* is the length of `self`
/// and *N* is the length of the result.
@warn_unused_result
@rethrows public func flatMap<T>(@noescape transform: (Self.Generator.Element) throws -> T?) rethrows -> [T]
}
结合亚特的建议,使用as?
代替is
你
可以用它作为
let myStructs:[MyProtocol] = [ TypeA(), TypeA(), TypeB() ]
let filteredArray = myStructs.flatMap { $0 as? TypeA }
现在,filteredArray
的类型被推断为[TypeA]
。
作为一种扩展方法,它将是
extension Array {
func objectsOfType<T>(type:T.Type) -> [T] {
return flatMap { $0 as? T }
}
}
let filteredArray = myStructs.objectsOfType(TypeA.self)
注意:对于 Swift&gt; = 4.1,将flatMap
替换为compactMap
。
答案 1 :(得分:4)
而不是测试(使用is
)如何投射(使用as
)?
let myStructs:[MyProtocol] = [ TypeA(), TypeA(), TypeB() ]
var filteredArray = [TypeA]()
for case let t as TypeA in myStructs {filteredArray.append(t)}
答案 2 :(得分:1)
转换数组在Swift中不起作用。这是因为Swift中的数组使用泛型,就像你不能转换自定义类一样,只有类型T
发生变化。 (class Custom<T>
,Custom<Int>() as! Custom<String>
)。
您可以做的是为Array创建一个扩展方法,您可以在其中定义如下方法:
extension Array {
func cast<TOut>() -> [TOut] {
var result: [TOut] = []
for item in self where item is TOut {
result.append(item as! TOut)
}
return result
}
}
答案 3 :(得分:0)
我认为规范的FP答案是使用过滤器,就像你一样,结合地图:
let filteredArray = myStructs.filter({ $0 is TypeA }).map({ $0 as! TypeA })
或者,您可以使用reduce:
let filtered2 = myStructs.reduce([TypeA]()) {
if let item = $1 as? TypeA {
return $0 + [item]
} else {
return $0
}
}
或者,因为它改变了一个数组,因此FP友好一些:
let filtered3 = myStructs.reduce([TypeA]()) { ( var array, value ) in
if let item = value as? TypeA {
array.append(item)
}
return array
}
实际上可以缩短为FP友好的flatMap:
let filtered4 = myStructs.flatMap { $0 as? TypeA }
并将其放在扩展名中:
extension Array {
func elementsWithType<T>() -> [T] {
return flatMap { $0 as? T }
}
}
let filtered5 : [TypeA] = myStructs.elementsWithType()