Swift Equatable on a protocol

前端 未结 10 2115
时光说笑
时光说笑 2021-01-30 12:42

I don\'t think this can be done but I\'ll ask anyway. I have a protocol:

protocol X {}

And a class:

class Y:X {}
10条回答
  •  别那么骄傲
    2021-01-30 13:33

    The reason why you should think twice about having a protocol conform to Equatable is that in many cases it just doesn't make sense. Consider this example:

    protocol Pet: Equatable {
      var age: Int { get }
    }
    
    extension Pet {
      static func == (lhs: Pet, rhs: Pet) -> Bool {
        return lhs.age == rhs.age
      }
    }
    
    struct Dog: Pet {
      let age: Int
      let favoriteFood: String
    }
    
    struct Cat: Pet {
      let age: Int
      let favoriteLitter: String
    }
    
    let rover: Pet = Dog(age: "1", favoriteFood: "Pizza")
    let simba: Pet = Cat(age: "1", favoriteLitter: "Purina")
    
    if rover == simba {
      print("Should this be true??")
    }
    

    You allude to type checking within the implementation of == but the problem is that you have no information about either of the types beyond them being Pets and you don't know all the things that might be a Pet (maybe you will add a Bird and Rabbit later). If you really need this, another approach can be modeling how languages like C# implement equality, by doing something like:

    protocol IsEqual {
      func isEqualTo(_ object: Any) -> Bool
    }
    
    protocol Pet: IsEqual {
      var age: Int { get }
    }
    
    struct Dog: Pet {
      let age: Int
      let favoriteFood: String
    
      func isEqualTo(_ object: Any) -> Bool {
        guard let otherDog = object as? Dog else { return false }
    
        return age == otherDog.age && favoriteFood == otherDog.favoriteFood
      }
    }
    
    struct Cat: Pet {
      let age: Int
      let favoriteLitter: String
    
      func isEqualTo(_ object: Any) -> Bool {
        guard let otherCat = object as? Cat else { return false }
    
        return age == otherCat.age && favoriteLitter == otherCat.favoriteLitter
      }
    }
    
    let rover: Pet = Dog(age: "1", favoriteFood: "Pizza")
    let simba: Pet = Cat(age: "1", favoriteLitter: "Purina")
    
    if !rover.isEqualTo(simba) {
      print("That's more like it.")
    }
    

    At which point if you really wanted, you could implement == without implementing Equatable:

    static func == (lhs: IsEqual, rhs: IsEqual) -> Bool { return lhs.isEqualTo(rhs) }
    

    One thing you would have to watch out for in this case is inheritance though. Because you could downcast an inheriting type and erase the information that might make isEqualTo not make logical sense.

    The best way to go though is to only implement equality on the class/struct themselves and use another mechanism for type checking.

提交回复
热议问题