Swift:在 [<SomeType<T>?] 上扩展以生成 [<T>?] 可能吗?
Swift: Extension on [<SomeType<T>?] to produce [<T>?] possible?
在 Swift 中,我有一个具有以下基本前提的自定义结构:
一个包装结构,可以包含任何符合BinaryInteger
的类型,例如Int、UInt8、Int16等
protocol SomeTypeProtocol {
associatedtype NumberType
var value: NumberType { get set }
}
struct SomeType<T: BinaryInteger>: SomeTypeProtocol {
typealias NumberType = T
var value: NumberType
}
以及 Collection 的扩展:
extension Collection where Element: SomeTypeProtocol {
var values: [Element.NumberType] {
return self.map { [=14=].value }
}
}
例如,这个效果很好:
let arr = [SomeType(value: 123), SomeType(value: 456)]
// this produces [123, 456] of type [Int] since literals are Int by default
arr.values
我想做的是完全相同的事情,但是 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)]
我已经尝试过无数次尝试来解决这个问题并进行了大量研究,但我希望任何一位圣人 Swift 退伍军人都能对此有所启发。
这是我设想的样子,但这行不通:
extension Collection where Element == Optional<SomeType<T>> {
var values: [T?] {
return self.map { [=17=]?.value }
}
}
这是在不使用泛型的情况下实现目标的笨拙方法,但它有效:
extension Collection where Element == Optional<SomeType<Int>> {
var values: [Int?] {
return self.map { [=18=]?.value }
}
}
let arr: [SomeType<Int>?] = [SomeType(value: 123), SomeType(value: 456)]
arr.values // [Optional(123), Optional(456)]
但它需要手动为每个符合 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>> { ... }
编辑 2018 年 6 月 23 日:
解决方案 #1 - 完全通用但必须是 Func,而不是计算 属性
扩展于 :
优点:如果 values()
变成 func
而不是计算 属性,这是一个优雅的解决方案。
缺点:没有已知的方法可以将此方法实现为计算属性,并且 Swift 的快速帮助弹出窗口在检查代码中的 values()
时显示 [T] 和 [T?]。即:它只是说 func values<T>() -> [T] where T : BinaryInteger
这不是很有用或 Swifty。然而,它当然仍然是强类型的。
extension Collection {
func values<T>() -> [T] where Element == SomeType<T> {
return map { [=20=].value }
}
func values<T>() -> [T?] where Element == SomeType<T>? {
return map { [=20=]?.value }
}
}
解决方案 #2 - 可选协议解决方法
扩展于 :
优点:允许使用计算属性(end-user 访问更干净,因为它不需要 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 { [=21=].value }
}
}
extension Collection where Element: OptionalType, Element.Wrapped: SomeTypeProtocol {
var values: [Element.Wrapped.NumberType?] {
return self.map { [=21=].asOptional?.value }
}
}
我不知道现在是否有更简单的解决方案,但是您可以使用与 and , the idea goes back to this Apple Forum Thread中相同的“技巧”。
首先定义一个所有可选项都遵循的协议:
protocol OptionalType {
associatedtype Wrapped
var asOptional: Wrapped? { get }
}
extension Optional : OptionalType {
var asOptional: Wrapped? {
return self
}
}
现在可以将所需的扩展定义为
extension Collection where Element: OptionalType, Element.Wrapped: SomeTypeProtocol {
var values: [Element.Wrapped.NumberType?] {
return self.map( { [=11=].asOptional?.value })
}
}
并且按预期工作:
let arr = [SomeType(value: 123), nil, SomeType(value: 456)]
let v = arr.values
print(v) // [Optional(123), Optional(456)]
print(type(of: v)) // Array<Optional<Int>>
是一个很好的解决方案。不需要额外标记协议的替代方法是:在 Collection
上编写 unconstrained 扩展,并在该扩展中定义一个通用函数 约束 到 where Element == SomeType<T>?
:
extension Collection {
func values<T>() -> [T?] where Element == SomeType<T>? {
return map( { [=10=]?.value })
}
}
这个有效:
let arr: [SomeType<Int>?] = [SomeType(value: 123), SomeType(value: 456)]
arr.values() // [Optional(123), Optional(456)]
您会注意到我使用了 func
而不是计算的 属性。我无法正确使用通用语法。这不是应该工作吗?
extension Collection {
// error: consecutive declarations on a line must be separated by ';'
var values<T>: [T?] where Element == SomeType<T>? {
return self.map( { [=12=]?.value })
}
}
在 Swift 中,我有一个具有以下基本前提的自定义结构:
一个包装结构,可以包含任何符合BinaryInteger
的类型,例如Int、UInt8、Int16等
protocol SomeTypeProtocol {
associatedtype NumberType
var value: NumberType { get set }
}
struct SomeType<T: BinaryInteger>: SomeTypeProtocol {
typealias NumberType = T
var value: NumberType
}
以及 Collection 的扩展:
extension Collection where Element: SomeTypeProtocol {
var values: [Element.NumberType] {
return self.map { [=14=].value }
}
}
例如,这个效果很好:
let arr = [SomeType(value: 123), SomeType(value: 456)]
// this produces [123, 456] of type [Int] since literals are Int by default
arr.values
我想做的是完全相同的事情,但是 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)]
我已经尝试过无数次尝试来解决这个问题并进行了大量研究,但我希望任何一位圣人 Swift 退伍军人都能对此有所启发。
这是我设想的样子,但这行不通:
extension Collection where Element == Optional<SomeType<T>> {
var values: [T?] {
return self.map { [=17=]?.value }
}
}
这是在不使用泛型的情况下实现目标的笨拙方法,但它有效:
extension Collection where Element == Optional<SomeType<Int>> {
var values: [Int?] {
return self.map { [=18=]?.value }
}
}
let arr: [SomeType<Int>?] = [SomeType(value: 123), SomeType(value: 456)]
arr.values // [Optional(123), Optional(456)]
但它需要手动为每个符合 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>> { ... }
编辑 2018 年 6 月 23 日:
解决方案 #1 - 完全通用但必须是 Func,而不是计算 属性
扩展于
优点:如果 values()
变成 func
而不是计算 属性,这是一个优雅的解决方案。
缺点:没有已知的方法可以将此方法实现为计算属性,并且 Swift 的快速帮助弹出窗口在检查代码中的 values()
时显示 [T] 和 [T?]。即:它只是说 func values<T>() -> [T] where T : BinaryInteger
这不是很有用或 Swifty。然而,它当然仍然是强类型的。
extension Collection {
func values<T>() -> [T] where Element == SomeType<T> {
return map { [=20=].value }
}
func values<T>() -> [T?] where Element == SomeType<T>? {
return map { [=20=]?.value }
}
}
解决方案 #2 - 可选协议解决方法
扩展于
优点:允许使用计算属性(end-user 访问更干净,因为它不需要 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 { [=21=].value }
}
}
extension Collection where Element: OptionalType, Element.Wrapped: SomeTypeProtocol {
var values: [Element.Wrapped.NumberType?] {
return self.map { [=21=].asOptional?.value }
}
}
我不知道现在是否有更简单的解决方案,但是您可以使用与
首先定义一个所有可选项都遵循的协议:
protocol OptionalType {
associatedtype Wrapped
var asOptional: Wrapped? { get }
}
extension Optional : OptionalType {
var asOptional: Wrapped? {
return self
}
}
现在可以将所需的扩展定义为
extension Collection where Element: OptionalType, Element.Wrapped: SomeTypeProtocol {
var values: [Element.Wrapped.NumberType?] {
return self.map( { [=11=].asOptional?.value })
}
}
并且按预期工作:
let arr = [SomeType(value: 123), nil, SomeType(value: 456)]
let v = arr.values
print(v) // [Optional(123), Optional(456)]
print(type(of: v)) // Array<Optional<Int>>
Collection
上编写 unconstrained 扩展,并在该扩展中定义一个通用函数 约束 到 where Element == SomeType<T>?
:
extension Collection {
func values<T>() -> [T?] where Element == SomeType<T>? {
return map( { [=10=]?.value })
}
}
这个有效:
let arr: [SomeType<Int>?] = [SomeType(value: 123), SomeType(value: 456)]
arr.values() // [Optional(123), Optional(456)]
您会注意到我使用了 func
而不是计算的 属性。我无法正确使用通用语法。这不是应该工作吗?
extension Collection {
// error: consecutive declarations on a line must be separated by ';'
var values<T>: [T?] where Element == SomeType<T>? {
return self.map( { [=12=]?.value })
}
}