如何使集合types约束generics?

我一直试图从string数组中提取非零值。 像下面一样。 但是,我的老大也希望能够从其他types中提取非零值

我看过, generics可以帮助我处理不同的types。 我怎样才能使用generics,以便我可以使用像扩展一样使用其他types?

getNonNil必须返回提取的非零值的特定types(即如果数组是[string?]它必须返回[string],返回[INT]如果[INT?]

因为我必须做进一步的计算。

我所尝试的是如下:

 import Foundation // Extended the collection-type so that collectiontype is constrained to having element with optional strings extension CollectionType where Self.Generator.Element == Optional<String>{ func getNonNil() -> [String] { // filter out all nil elements and forcefully unwrap them using map return self.filter({$0 != nil}).map({$0!}) } } // Usage let x: [String?] = ["Er", "Err", nil, "errr"] x.getNonNil().forEach { (str) in print(str) } 

因为你不能在扩展中引入新的genericstypes(虽然这是Swiftgenerics声明的一部分 – 所以很可能在Swift的未来版本中)。

正如@kennytm所说 ,最简单的解决scheme就是使用flatMap ,它会过滤掉nil

 x.flatMap{$0}.forEach { (str) in print(str) } 

但是,如果您仍然希望将其作为扩展名,则可以使用协议解决方法,以便将扩展名限制为任何可选元素types(Swift 3):

 protocol _OptionalProtocol { associatedtype Wrapped func _asOptional() -> Wrapped? } extension Optional : _OptionalProtocol { func _asOptional() -> Wrapped? {return self} } extension Collection where Self.Iterator.Element : _OptionalProtocol { func getNonNil() -> [Iterator.Element.Wrapped] { return flatMap{$0._asOptional()} } } ... let x : [String?] = ["Er", "Err", nil, "errr"] x.getNonNil().forEach { (str) in print(str) } 

(在Swift 3中, CollectionType已经被重命名为CollectionGenerator现在是Iterator

虽然flatMap在这种情况下几乎可以肯定是首选,但我只是为了完成而添加这个。

对于getNonNil你可以简单地使用

 x.flatMap { $0 } // returns ["Er", "Err", "errr"] which is [String] 

对于原始问题,通常你可以引入一个可选types的协议(例如通过muukii / OptionalProtocol包):

 protocol OptionalProtocol { associatedtype Wrapped var value: Wrapped? { get } } extension Optional: OptionalProtocol { public var value: Wrapped? { return self } } extension CollectionType where Self.Generator.Element: OptionalProtocol { func getNonNil() -> [Self.Generator.Element.Wrapped] { ... } } 

最简单的方法是使用flatMap作为kennytm的build议,但是如果您绝对想知道如何使用generics创build这样的方法,一种方法是创build一个全局方法,将集合作为参数:

 public func getNonNil<T, C: CollectionType where C.Generator.Element == Optional<T>>(collection: C) -> [T] { return collection.filter({$0 != nil}).map({$0!}) } let x: [String?] = ["Er", "Err", nil, "errr"] print(getNonNil(x)) // returns ["Er", "Err", "errr"]