【问题标题】:Add constraints to generic parameters in extension在扩展中为泛型参数添加约束
【发布时间】:2015-10-30 12:39:14
【问题描述】:

我有这个功能:

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 类,该方法仅适用于值为任何类型的 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>'

【问题讨论】:

    标签: swift generics dictionary swift2 swift-extensions


    【解决方案1】:

    在 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 唯一符合协议(例如OptionalEquivalent)。

    为了得到Optional的封装值类型,我在自定义协议WrappedValueType中定义了一个typealiasOptionalEquivalent,然后做了Optional的扩展,将Wrapped赋值为WrappedValueType,然后您可以在 flatten 方法中获取类型。

    注意sugarCast 方法只是将Optional&lt;Wrapped&gt; 转换为Wrapped?(完全一样),以启用guard 语句的用法。

    更新

    感谢 Rob Napier 的评论,我已简化并重命名了 sugarCast() 方法并重命名了协议以使其更易于理解。

    【讨论】:

    • 您可以更轻松地将sugarCast() 实现为return self。我个人建议将此协议称为 OptionalConvertiblesugarCast() toOptional(),但您的方式也很好。
    • 这是一种非常有创意的解决通用限制的方法。我可能会在更多地方使用它。谢谢
    【解决方案2】:

    你可以用更简单的方法来做。这适用于 Swift 4:

    extension Dictionary {
        func flatten<Wrapped>() -> [Key: Wrapped] where Value == Optional<Wrapped> {
             return filter { $1 != nil }.mapValues { $0! }
        }
    }
    

    如果您不喜欢使用高阶函数或需要与以前版本的 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
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-23
      • 2019-03-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多