swift – 检查Hashable一致性

前端之家收集整理的这篇文章主要介绍了swift – 检查Hashable一致性前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我有一个基本协议(模型),一些结构符合.它们也符合Hashable
  1. protocol Model {}
  2. struct Contact: Model,Hashable {
  3. var hashValue: Int { return ... }
  4. static func ==(lhs: Contact,rhs: Contact) -> Bool { return ... }
  5. }
  6. struct Address: Model,Hashable {
  7. var hashValue: Int { return ... }
  8. static func ==(lhs: Address,rhs: Address) -> Bool { return ... }
  9. }

我有一个函数,它接受一个符合Model([Model])的对象数组.
如何将[Model]传递给需要Hashables而不制作Model Hashable的函数

  1. func complete(with models: [Model]) {
  2. doSomethingWithHashable(models) //can't do this
  3. }
  4. func doSomethingWithHashable <T:Hashable>(_ objects: [T]) {
  5. //
  6. }

我试图避免这种情况

  1. protocol Model: Hashable {}
  2. func complete<T:Model>(with models: [T]) {
  3. runComparison(models)
  4. }

因为当我这样做时,我得到“模型不能用作通用约束……”

  1. protocol SomethingElse {
  2. var data: [Model] { get }
  3. }
你的代码的问题在于你在谈论Model,它对Hashable一致性没有任何承诺.正如您所指出的那样,告诉编译器关于此问题(即从Hashable派生模型)的问题是,您失去了根据符合模型的异构类型进行交谈的能力.

如果您首先不关心模型一致性,则可以使用标准库的AnyHashable类型擦除包装器来完全任意Hashable一致性实例.

但是,假设您关心模型一致性,则必须为符合Model和Hashable的实例构建自己的type-erased wrapper.在my answer here中,我演示了如何为Equatable标准类型构建类型橡皮擦.可以很容易地为Hashable扩展逻辑 – 我们只需要存储一个额外的函数来返回实例的hashValue.

例如:

  1. struct AnyHashableModel : Model,Hashable {
  2.  
  3. static func ==(lhs: AnyHashableModel,rhs: AnyHashableModel) -> Bool {
  4.  
  5. // forward to both lhs's and rhs's _isEqual in order to determine equality.
  6. // the reason that both must be called is to preserve symmetry for when a
  7. // superclass is being compared with a subclass.
  8. // if you know you're always working with value types,you can omit one of them.
  9. return lhs._isEqual(rhs) || rhs._isEqual(lhs)
  10. }
  11.  
  12. private let base: Model
  13.  
  14. private let _isEqual: (_ to: AnyHashableModel) -> Bool
  15. private let _hashValue: () -> Int
  16.  
  17. init<T : Model>(_ base: T) where T : Hashable {
  18.  
  19. self.base = base
  20.  
  21. _isEqual = {
  22. // attempt to cast the passed instance to the concrete type that
  23. // AnyHashableModel was initialised with,returning the result of that
  24. // type's == implementation,or false otherwise.
  25. if let other = $0.base as? T {
  26. return base == other
  27. } else {
  28. return false
  29. }
  30. }
  31.  
  32. // simply assign a closure that captures base and returns its hashValue
  33. _hashValue = { base.hashValue }
  34. }
  35.  
  36. var hashValue: Int { return _hashValue() }
  37. }

然后你会像这样使用它:

  1. func complete(with models: [AnyHashableModel]) {
  2. doSomethingWithHashable(models)
  3. }
  4.  
  5. func doSomethingWithHashable<T : Hashable>(_ objects: [T]) {
  6. //
  7. }
  8.  
  9. let models = [AnyHashableModel(Contact()),AnyHashableModel(Address())]
  10. complete(with: models)

在这里,我假设您还希望将其用作Model的要求的包装(假设有一些).或者,您可以公开base属性并从AnyHashableModel本身中删除Model一致性,使调用者访问基础Model符合实例的基础:

  1. struct AnyHashableModel : Hashable {
  2. // ...
  3. let base: Model
  4. // ...
  5. }

但是,您会注意到上述类型擦除的包装器仅适用于Hashable和Model的类型.如果我们想谈论符合实例Hashable的其他协议怎么办?

正如我演示in this Q&A,更通用的解决方案是接受Hashable并且符合其他协议的类型 – 其类型由通用占位符表示.

因为Swift目前还没有办法表达一个通用占位符,它必须符合另一个通用占位符给出的协议;必须由调用者使用转换闭包来定义此关系,以执行必要的向上转换.但是,由于Swift 3.1接受扩展中的具体相同类型要求,我们可以定义一个方便初始化器来删除Model的样板(这可以在其他协议类型中重复).

例如:

  1. /// Type-erased wrapper for a type that conforms to Hashable,/// but inherits from/conforms to a type T that doesn't necessarily require
  2. /// Hashable conformance. In almost all cases,T should be a protocol type.
  3. struct AnySpecificHashable<T> : Hashable {
  4.  
  5. static func ==(lhs: AnySpecificHashable,rhs: AnySpecificHashable) -> Bool {
  6. return lhs._isEqual(rhs) || rhs._isEqual(lhs)
  7. }
  8.  
  9. let base: T
  10.  
  11. private let _isEqual: (_ to: AnySpecificHashable) -> Bool
  12. private let _hashValue: () -> Int
  13.  
  14. init<U : Hashable>(_ base: U,upcast: (U) -> T) {
  15.  
  16. self.base = upcast(base)
  17.  
  18. _isEqual = {
  19. if let other = $0.base as? U {
  20. return base == other
  21. } else {
  22. return false
  23. }
  24. }
  25.  
  26. _hashValue = { base.hashValue }
  27. }
  28. var hashValue: Int { return _hashValue() }
  29. }
  30.  
  31. // extension for convenience initialiser for when T is Model.
  32. extension AnySpecificHashable where T == Model {
  33. init<U : Model>(_ base: U) where U : Hashable {
  34. self.init(base,upcast: { $0 })
  35. }
  36. }

您现在想要将您的实例包装在AnySpecificHashable< Model>中:

  1. func complete(with models: [AnySpecificHashable<Model>]) {
  2. doSomethingWithHashable(models)
  3. }
  4.  
  5. func doSomethingWithHashable<T : Hashable>(_ objects: [T]) {
  6. //
  7. }
  8.  
  9. let models: [AnySpecificHashable<Model>] = [
  10. AnySpecificHashable(Contact()),AnySpecificHashable(Address())
  11. ]
  12.  
  13. complete(with: models)

猜你在找的Swift相关文章