Swift 4:扩展数组<Optional <SomeType <T >>>生成数组<Optional <T >>可能吗?

ste*_*tef 5 arrays generics optional swift swift-extensions

在Swift 4中,我有一个带有这个基本前提的自定义结构:

一个包装器结构,可以包含任何符合BinaryIntegerInt,UInt8,Int16等类型的类型.

protocol SomeTypeProtocol {
    associatedtype NumberType

    var value: NumberType { get set }
}

struct SomeType<T: BinaryInteger>: SomeTypeProtocol {
    typealias NumberType = T

    var value: NumberType
}
Run Code Online (Sandbox Code Playgroud)

收藏的扩展:

extension Collection where Element: SomeTypeProtocol {
    var values: [Element.NumberType] {
        return self.map { $0.value }
    }
}
Run Code Online (Sandbox Code Playgroud)

例如,这很好用:

let arr = [SomeType(value: 123), SomeType(value: 456)]

// this produces [123, 456] of type [Int] since literals are Int by default
arr.values
Run Code Online (Sandbox Code Playgroud)

我想做的是完全相同的事情,但是 SomeType<T>?

let arr: [SomeType<Int>?] = [SomeType(value: 123), SomeType(value: 456)]

// this doesn't work, obviously
arr.values

// but what I want is this:
arr.values // to produce [Optional(123), Optional(456)]
Run Code Online (Sandbox Code Playgroud)

我已经尝试了很多尝试来解决这个问题以及相当多的研究,但我希望任何一位圣人Swift老兵都可以对此有所了解.

这是我想象的样子,但这不起作用:

extension Collection where Element == Optional<SomeType<T>> {
    var values: [T?] {
        return self.map { $0?.value }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是一种在不使用泛型的情况下实现目标的笨拙方式,它可以工作:

extension Collection where Element == Optional<SomeType<Int>> {
    var values: [Int?] {
        return self.map { $0?.value }
    }
}

let arr: [SomeType<Int>?] = [SomeType(value: 123), SomeType(value: 456)]
arr.values // [Optional(123), Optional(456)]
Run Code Online (Sandbox Code Playgroud)

但它需要为符合BinaryInteger的每种已知类型手动编写扩展,并且不会自动包含采用BinaryInteger的未来类型,而无需手动更新代码.

// "..." would contain the var values code from above, copy-and-pasted
extension Collection where Element == Optional<SomeType<Int>> { ... }
extension Collection where Element == Optional<SomeType<Int8>> { ... }
extension Collection where Element == Optional<SomeType<UInt8>> { ... }
extension Collection where Element == Optional<SomeType<Int16>> { ... }
extension Collection where Element == Optional<SomeType<UInt16>> { ... }
extension Collection where Element == Optional<SomeType<Int32>> { ... }
extension Collection where Element == Optional<SomeType<UInt32>> { ... }
extension Collection where Element == Optional<SomeType<Int64>> { ... }
extension Collection where Element == Optional<SomeType<UInt64>> { ... }
Run Code Online (Sandbox Code Playgroud)

编辑2018年6月23日:

解决方案#1 - 完全通用但必须是功能,非计算属性

Ole的回复扩大了:

优点:如果values()成为一个func而不是计算属性,这是一个优雅的解决方案.

缺点:没有已知的方法将此方法实现为计算属性,并且在检查values()代码时,Swift的快速帮助弹出窗口显示[T]和[T?] .即:它只是说func values<T>() -> [T] where T : BinaryInteger哪些不是非常有用的信息或Swifty.然而,当然它仍然是强类型的.

extension Collection {
    func values<T>() -> [T] where Element == SomeType<T> {
        return map { $0.value }
    }

    func values<T>() -> [T?] where Element == SomeType<T>? {
        return map { $0?.value }
    }
}
Run Code Online (Sandbox Code Playgroud)

解决方案#2 - 可选协议解决方法

扩大了Martin的回复:

优点:允许使用计算属性(最终用户可以访问清洁,因为它不需要func parens),并在Xcode的快速帮助弹出窗口中显示推断类型.

缺点:从内部代码的角度来看,并不优雅,因为它需要一种解决方法.但不一定是缺点.

// Define OptionalType

protocol OptionalType {
    associatedtype Wrapped
    var asOptional: Wrapped? { get }
}

extension Optional: OptionalType {
    var asOptional: Wrapped? {
        return self
    }
}

// Extend Collection

extension Collection where Element: SomeTypeProtocol {
    var values: [Element.NumberType] {
        return self.map { $0.value }
    }
}

extension Collection where Element: OptionalType, Element.Wrapped: SomeTypeProtocol {
    var values: [Element.Wrapped.NumberType?] {
        return self.map { $0.asOptional?.value }
    }
}
Run Code Online (Sandbox Code Playgroud)

Ole*_*ann 9

Martin R的答案是一个很好的解决方案.另一种不需要额外标记协议的方法是:在上面写一个无约束的扩展Collection,并在该扩展中定义一个约束where Element == SomeType<T>?:

extension Collection {
    func values<T>() -> [T?] where Element == SomeType<T>? {
        return map( { $0?.value })
    }
}
Run Code Online (Sandbox Code Playgroud)

这有效:

let arr: [SomeType<Int>?] = [SomeType(value: 123), SomeType(value: 456)]
arr.values() // [Optional(123), Optional(456)]
Run Code Online (Sandbox Code Playgroud)

你会注意到我使用的是一个func而不是一个计算属性.我无法正确使用通用语法.这应该不起作用吗?

extension Collection {
    // error: consecutive declarations on a line must be separated by ';'
    var values<T>: [T?] where Element == SomeType<T>? {
        return self.map( { $0?.value })
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 那很优雅! (3认同)
  • 这是出色的,当然也是最优雅的,感谢您抽出宝贵的时间回复。(有关完整实现的信息,请参见我的原始帖子中的编辑。)我接受了马丁的回答,尽管它并不那么优雅,它仍然允许使用计算属性,并且具有在Swift的快速帮助中显示推断类型的好处,而不是[T] / [T?]。 (2认同)

Mar*_*n R 4

我不知道现在是否有更简单的解决方案,但是您可以使用与How can I write a function that will unwrapp a generic property in swift 中相同的 \xe2\x80\x9ctrick\xe2\x80\x9d 假设它是可选类型吗?创建一个扩展来从 Swift 中的数组中过滤 nils,这个想法可以追溯到这个 Apple 论坛主题

\n\n

首先定义一个所有选项都遵循的协议:

\n\n
protocol OptionalType {\n    associatedtype Wrapped\n    var asOptional: Wrapped? { get }\n}\n\nextension Optional : OptionalType {  \n    var asOptional: Wrapped? {  \n        return self \n    }  \n}  \n
Run Code Online (Sandbox Code Playgroud)\n\n

现在所需的扩展可以定义为

\n\n
extension Collection where Element: OptionalType, Element.Wrapped: SomeTypeProtocol {\n    var values: [Element.Wrapped.NumberType?] {\n        return self.map( { $0.asOptional?.value })\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

并且按预期工作:

\n\n
let arr = [SomeType(value: 123), nil, SomeType(value: 456)]\nlet v = arr.values\n\nprint(v) // [Optional(123), Optional(456)]\nprint(type(of: v)) // Array<Optional<Int>>\n
Run Code Online (Sandbox Code Playgroud)\n