arrays – 在实现Equatable的结构数组上的操作

前端之家收集整理的这篇文章主要介绍了arrays – 在实现Equatable的结构数组上的操作前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我有一组不同的结构,都实现了Equatable协议,我试图将它传递给一个需要T.Iterator.Element:Equatable集合的函数.我知道如何通过使用类来解决这个问题,只需创建一个类:Vehicle:Identifiable,Equatable,然后使Car和Tractor实现Vehicle.但是,我想知道使用结构和协议是否可行?

这是我正在尝试做的一个人为的例子

  1. //: Playground - noun: a place where people can play
  2.  
  3. protocol Identifiable {
  4. var ID: String { get set }
  5. init(ID: String)
  6. init()
  7. }
  8.  
  9. extension Identifiable {
  10. init(ID: String) {
  11. self.init()
  12. self.ID = ID
  13. }
  14. }
  15.  
  16. typealias Vehicle = Identifiable & Equatable
  17.  
  18. struct Car: Vehicle {
  19. var ID: String
  20.  
  21. init() {
  22. ID = ""
  23. }
  24.  
  25. public static func ==(lhs: Car,rhs: Car) -> Bool {
  26. return lhs.ID == rhs.ID
  27. }
  28. }
  29.  
  30. struct Tractor: Vehicle {
  31. var ID: String
  32.  
  33. init() {
  34. ID = ""
  35. }
  36.  
  37. public static func ==(lhs: Tractor,rhs: Tractor) -> Bool {
  38. return lhs.ID == rhs.ID
  39. }
  40. }
  41.  
  42. class Operator {
  43. func operationOnCollectionOfEquatables<T: Collection>(array: T) where T.Iterator.Element: Equatable {
  44. }
  45. }
  46.  
  47. var array = [Vehicle]() //Protocol 'Equatable' can only be used as a generic constraint because Self or associated type requirements
  48.  
  49. array.append(Car(ID:"VW"))
  50. array.append(Car(ID:"Porsche"))
  51. array.append(Tractor(ID:"John Deere"))
  52. array.append(Tractor(ID:"Steyr"))
  53.  
  54. var op = Operator()
  55. op.operationOnCollectionOfEquatables(array: array) //Generic parameter 'T' could not be inferred
问题是,正如错误所述,您不能将具有Self或相关类型要求的协议用作实际类型 – 因为您丢失了这些要求的类型信息.在这种情况下,您将丢失==实现的参数的类型信息 – 正如Equatable所说,它们必须与符合类型(即Self)的类型相同.

解决方案几乎总是构建一个type eraser.如果期望类型相等,如果它们的id属性相等,这可以像存储id属性并在==实现中进行比较一样简单.

  1. struct AnyVehicle : Equatable {
  2.  
  3. static func ==(lhs: AnyVehicle,rhs: AnyVehicle) -> Bool {
  4. return lhs.id == rhs.id
  5. }
  6.  
  7. let id : String
  8.  
  9. init<T : Vehicle>(_ base: T) {
  10. id = base.id
  11. }
  12. }

(注意我将您的ID属性重命名为id以符合Swift命名约定)

但是,更通用的解决方案是在类型擦除器中存储一个函数,它可以在类型转换后根据它们的==实现比较两个任意的车辆符合实例,以确保它们与类型橡皮擦的具体类型相同是用.创建的.

  1. struct AnyVehicle : Equatable {
  2.  
  3. static func ==(lhs: AnyVehicle,rhs: AnyVehicle) -> 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. let base: Identifiable
  13.  
  14. private let _isEqual: (_ to: AnyVehicle) -> Bool
  15.  
  16. init<T : Vehicle>(_ base: T) {
  17.  
  18. self.base = base
  19.  
  20. _isEqual = {
  21.  
  22. // attempt to cast the passed instance to the concrete type that
  23. // AnyVehicle 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. }
  1. print(AnyVehicle(Car(id: "foo")) == AnyVehicle(Tractor(id: "foo"))) // false
  2. print(AnyVehicle(Car(id: "foo")) == AnyVehicle(Car(id: "bar"))) // false
  3. print(AnyVehicle(Car(id: "foo")) == AnyVehicle(Car(id: "foo"))) // true
  4.  
  5. var array = [AnyVehicle]()
  6.  
  7. array.append(AnyVehicle(Car(id: "VW")))
  8. array.append(AnyVehicle(Car(id: "Porsche")))
  9. array.append(AnyVehicle(Tractor(id: "John Deere")))
  10. array.append(AnyVehicle(Tractor(id: "Steyr")))
  11.  
  12. var op = Operator()
  13.  
  14. // compiles fine as AnyVehicle conforms to Equatable.
  15. op.operationOnCollectionOfEquatables(array: array)

猜你在找的Swift相关文章