向扩展中的泛型参数添加约束
Add constraints to generic parameters in extension
我有这个功能:
func flatten<Key: Hashable, Value>(dict: Dictionary<Key, Optional<Value>>) -> Dictionary<Key, Value> {
var result = [Key: Value]()
for (key, value) in dict {
guard let value = value else { continue }
result[key] = value
}
return result
}
如您所见,它将 [Key: Value?]
字典转换为 [Key: Value]
字典(没有可选)。
我想用一种新方法扩展 Dictionary
class 仅适用于 classes 其值是任何类型的 Optional
,但我无法为字典的通用参数添加约束。
这是我试过的:
extension Dictionary where Value: Optional<Any> {
func flatten() -> [Key: Any] {
var result = [Key: Any]()
for (key, value) in self {
guard let value = value else { continue }
result[key] = value
}
return result
}
}
但因错误而失败:
Type 'Value' constrained to non-protocol type 'Optional<Any>'
在 Playground 中尝试此代码:
// make sure only `Optional` conforms to this protocol
protocol OptionalEquivalent {
typealias WrappedValueType
func toOptional() -> WrappedValueType?
}
extension Optional: OptionalEquivalent {
typealias WrappedValueType = Wrapped
// just to cast `Optional<Wrapped>` to `Wrapped?`
func toOptional() -> WrappedValueType? {
return self
}
}
extension Dictionary where Value: OptionalEquivalent {
func flatten() -> Dictionary<Key, Value.WrappedValueType> {
var result = Dictionary<Key, Value.WrappedValueType>()
for (key, value) in self {
guard let value = value.toOptional() else { continue }
result[key] = value
}
return result
}
}
let a: [String: String?] = ["a": "a", "b": nil, "c": "c", "d": nil]
a.flatten() //["a": "a", "c": "c"]
因为您不能在协议扩展的 where
子句中指定确切的类型,所以您可以准确检测 Optional
类型的一种方法是使 Optional
UNIQUELY 符合协议(比如 OptionalEquivalent
)。
为了得到Optional
的wrapped值类型,我在自定义协议OptionalEquivalent
中定义了一个类型别名WrappedValueType
,然后对Optional进行了扩展,assgin Wrapped
到 WrappedValueType
,然后就可以在 flatten 方法中得到类型了。
请注意,sugarCast
方法只是将 Optional<Wrapped>
转换为 Wrapped?
(完全相同),以启用使用 guard
语句。
更新
感谢 Rob Napier 的评论,我简化并重命名了 sugarCast() 方法并重命名了协议以使其更易于理解。
你可以用更简单的方法来做。这适用于 Swift 4:
extension Dictionary {
func flatten<Wrapped>() -> [Key: Wrapped] where Value == Optional<Wrapped> {
return filter { != nil }.mapValues { [=10=]! }
}
}
如果您不喜欢使用高阶函数或需要与以前版本的 Swift 兼容,您也可以这样做:
extension Dictionary {
func flatten<Wrapped>() -> [Key: Wrapped] where Value == Optional<Wrapped> {
var result: [Key: Wrapped] = [:]
for (key, value) in self {
guard let value = value else { continue }
result[key] = value
}
return result
}
}
我有这个功能:
func flatten<Key: Hashable, Value>(dict: Dictionary<Key, Optional<Value>>) -> Dictionary<Key, Value> {
var result = [Key: Value]()
for (key, value) in dict {
guard let value = value else { continue }
result[key] = value
}
return result
}
如您所见,它将 [Key: Value?]
字典转换为 [Key: Value]
字典(没有可选)。
我想用一种新方法扩展 Dictionary
class 仅适用于 classes 其值是任何类型的 Optional
,但我无法为字典的通用参数添加约束。
这是我试过的:
extension Dictionary where Value: Optional<Any> {
func flatten() -> [Key: Any] {
var result = [Key: Any]()
for (key, value) in self {
guard let value = value else { continue }
result[key] = value
}
return result
}
}
但因错误而失败:
Type 'Value' constrained to non-protocol type 'Optional<Any>'
在 Playground 中尝试此代码:
// make sure only `Optional` conforms to this protocol
protocol OptionalEquivalent {
typealias WrappedValueType
func toOptional() -> WrappedValueType?
}
extension Optional: OptionalEquivalent {
typealias WrappedValueType = Wrapped
// just to cast `Optional<Wrapped>` to `Wrapped?`
func toOptional() -> WrappedValueType? {
return self
}
}
extension Dictionary where Value: OptionalEquivalent {
func flatten() -> Dictionary<Key, Value.WrappedValueType> {
var result = Dictionary<Key, Value.WrappedValueType>()
for (key, value) in self {
guard let value = value.toOptional() else { continue }
result[key] = value
}
return result
}
}
let a: [String: String?] = ["a": "a", "b": nil, "c": "c", "d": nil]
a.flatten() //["a": "a", "c": "c"]
因为您不能在协议扩展的 where
子句中指定确切的类型,所以您可以准确检测 Optional
类型的一种方法是使 Optional
UNIQUELY 符合协议(比如 OptionalEquivalent
)。
为了得到Optional
的wrapped值类型,我在自定义协议OptionalEquivalent
中定义了一个类型别名WrappedValueType
,然后对Optional进行了扩展,assgin Wrapped
到 WrappedValueType
,然后就可以在 flatten 方法中得到类型了。
请注意,sugarCast
方法只是将 Optional<Wrapped>
转换为 Wrapped?
(完全相同),以启用使用 guard
语句。
更新
感谢 Rob Napier 的评论,我简化并重命名了 sugarCast() 方法并重命名了协议以使其更易于理解。
你可以用更简单的方法来做。这适用于 Swift 4:
extension Dictionary {
func flatten<Wrapped>() -> [Key: Wrapped] where Value == Optional<Wrapped> {
return filter { != nil }.mapValues { [=10=]! }
}
}
如果您不喜欢使用高阶函数或需要与以前版本的 Swift 兼容,您也可以这样做:
extension Dictionary {
func flatten<Wrapped>() -> [Key: Wrapped] where Value == Optional<Wrapped> {
var result: [Key: Wrapped] = [:]
for (key, value) in self {
guard let value = value else { continue }
result[key] = value
}
return result
}
}